#!/bin/ksh # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos720 src/bos/usr/lib/nim/methods/dd_funcs.sh 1.1.1.3 # # Licensed Materials - Property of IBM # # Restricted Materials of IBM # # COPYRIGHT International Business Machines Corp. 2009,2011 # 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 # @(#)52 1.1.1.3 src/bos/usr/lib/nim/methods/dd_funcs.sh, cmdnim, bos720 7/14/11 20:15:58 # COMPONENT_NAME: CMDNIM # # FUNCTIONS: ./usr/lib/nim/methods/dd_funcs.sh # # ORIGINS: 83 # #---------------------------- local defines -------------------------------- FS_STANZAS="/ root:/usr spot ro:/home home:/tmp tmp:/var/adm/ras log:/var var:/opt opt:/proc proc:/admin admin" STRIP_FS="/: /home: /usr: /var: /tmp: /opt: /proc:" INST_ROOT="lpp/bos/inst_root" ATTR_LOCATION=location ATTR_PERMS=perms ATTR_NFSPARAMS=nfsparams STATE_RUNNING="running" NIMCLIENT_ITAB="nimclient:2:wait:${NIMCLIENT} -S ${STATE_RUNNING} \ >/dev/console 2>&1 # NIM Mstate" NIMCLIENT2_ITAB="nimclientR:2:once:${NIMCLIENT} -R success \ >/dev/console 2>&1 # NIM Cstate" RECFGCT_ITAB="recfgct:2:boot:/usr/sbin/rsct/install/bin/recfgct \ >/dev/console 2>&1 # Reconfigure the RSCT subsystems" CUTFS=${NIM_AWK}/cutfs.awk #---------------------------- module globals -------------------------------- name="" hostname="" boot_info="" adpt_name="" server="" root="" spot="" dump="" master="" type="" paging="" ring_speed="" cable_type="" iplrom_emu="" home="" tmp="" log="" license_msg="diskless/dataless" saved_name="" shared_root="" location="" nfs_domain="" #---------------------------- undo -------------------------------- # # NAME: undo # # FUNCTION: # removes a newly created root directory # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1 = optional; if present, name of failing function # global: # # RETURNS: (int) # # OUTPUT: #------------------------------------------------------------------------------- function undo { typeset func=${1} # cache current error [[ -n "${func}" ]] && ${CP} ${ERR} ${TMPDIR}/errstr 2>/dev/null # remove all files in the root ${RM} -r ${root}/* 2>${ERR} || warning_from_cmd "${RM} -r ${root}" if [[ -n "${func}" ]] then ${CP} ${TMPDIR}/errstr ${ERR} err_from_cmd ${func} else error ${ERR_SYS} "$( ${CAT} ${ERR} )" fi } # end of undo #---------------------------- strip_stanzas -------------------------------- # # NAME: strip_stanzas # # FUNCTION: # removes the specified stanzas from the specified file # # EXECUTION ENVIRONMENT: # # NOTES: # ASSUMES that stanzas are structured as follows: # 1) beginning of stanza can be matched with /^.*:$/ # 2) 1st blank line after stanza beginning terminates the stanza # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1 = file to operate on # 2 = one or more stanza beginnings (separated by " ") # or the keyword "all" # global: # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function strip_stanzas { typeset file=${1} typeset stanzas=${2} [[ ! -r "${file}" ]] && return 1 if ${AWK} -v s="${stanzas}" '\ BEGIN{num=split(s,sa);};\ function found(a){for(i in sa){if(sa[i]==a)return 1;};return 0;};\ /^.+:$/{in_stanza=((s=="all") || (found($1)>0));};\ /^[ ]*$/{in_stanza=0};\ in_stanza==0 {print;}' ${file} >${TMPDIR}/stripd 2>${ERR} then ${CAT} ${TMPDIR}/stripd >${file} 2>${ERR} && return 0 fi return 1 } # end of strip_stanzas #*---------------------------- pop_root ------------------------------ # # NAME: pop_root # # FUNCTION: # creates a new root directory # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # root # # RETURNS: (int) # 0 = no errors # # OUTPUT: #-----------------------------------------------------------------------------*/ function pop_root { typeset inst_root="" typeset i="" # root dir must already exist (if not, there was an error during allocation) [[ ! -d ${root} ]] && error ${ERR_FILE_ACCESS} ${root} # make sure root has correct permissions ${CHMOD} 755 ${root} 2>${ERR} || undo "${CHMOD} 755 ${root}" # SPOT local or remote? if [[ -n "${spot_host}" ]] then # mount the SPOT nim_mount "${spot_host}:${spot}" spot=${access_pnt} fi inst_root="${spot}/${INST_ROOT}" # go to the INST_ROOT cd ${inst_root} 2>${ERR} || err_from_cmd "cd ${inst_root}" # root directory already populated? if [[ -n "$(${LS} ${root} 2>/dev/null)" ]] then # generate list of files which do exist in INST_ROOT but don't exist # in root yet >${TMPDIR}/new_files for i in $( ${FIND} . -print ) do [[ ! -r ${root}/${i} ]] && print ${i} >>${TMPDIR}/new_files done # copy the new files if [[ -s ${TMPDIR}/new_files ]] then ${CAT} ${TMPDIR}/new_files | \ ${CPIO} -pd ${root} 2>${ERR} || undo ${CPIO} fi else # copy all the INST_ROOT files ${FIND} . -print | ${CPIO} -pd ${root} 2>${ERR} || undo ${CPIO} fi return 0 } # end of pop_root #*---------------------------- add_host ------------------------------ # # NAME: add_host # # FUNCTION: # adds the specified host to the client's /etc/hosts file # this function prevents duplicate entries from being added # # EXECUTION ENVIRONMENT: # # NOTES: # this function ASSUMES that the current working directory is the client's # root directory # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1 = hostname to add # global: # ip = IP address of specified hostname # # RETURNS: (int) # 0 = no errors # >0 = failure # # OUTPUT: #-----------------------------------------------------------------------------*/ function add_host { # local variables typeset hostname=${1} typeset hosts="${root}${HOSTS}" typeset name="" typeset alias="" typeset yes="" typeset ip="" # If we are using a shared root resource, then fill a specific # hosts file [[ -n "${shared_root}" ]] && hosts="${hosts}.${saved_name}" # is host already in the file? [[ -s ${hosts} ]] && \ yes=$(${AWK} -v host=${hostname} '$2==host || $3==host {print}' ${hosts}) if [[ -z "${yes}" ]] then # not there already # resolve it to an IP address ${HOSTCMD} ${hostname} >${TMPDIR}/host 2>${ERR} || return 1 # is the resolved name different from $hostname? name=$(${AWK} '{print $1}' ${TMPDIR}/host) if [[ -n "${name}" ]] && [[ "${name}" != "${hostname}" ]] then # treat $hostname as an alias alias=${hostname} hostname=${name} fi # get IP address ip=$( ${AWK} '{gsub(/,/,"");print $3}' ${TMPDIR}/host 2>${ERR} ) # add an entry if [[ -n "${ip}" ]] && [[ -n "${hostname}" ]] then print "${ip}\t${hostname}\t${alias}" >> ${hosts} else print "unable to resolve \"${hostname}\" to an IP address" >${ERR} return 1 fi fi # success return 0 } # add_host #*---------------------------- add_fs_stanza ------------------------------ # # NAME: add_fs_stanza # # FUNCTION: # adds the specified filesystems stanza # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1 = local mount point # 2 = variable prefix for remote mount info # 3 = filesystems options # global: # root # # RETURNS: (int) # 0 = no errors # >0 = failure # # OUTPUT: #-----------------------------------------------------------------------------*/ function add_fs_stanza { # local variables typeset mntpnt=${1} typeset prefix=${2} eval typeset dir=\$${prefix} eval typeset host=\$${prefix}"_host" eval typeset nfs_version=\$${prefix}"_vers" host=${host:-${server}} typeset options=${3:-"rw"} typeset fs="${root}${FILESYSTEMS}" # If we are using a shared root resource, then fill a specific # filesystems file [[ -n "${shared_root}" ]] && fs="${fs}.${name}" # is resource nfs4-exported ? [[ "${nfs_version}" = "4" ]] && options="vers=4,${options}" # remove the filesystems stanza for this mount point if it exists if ${AWK} -v mntpnt=${mntpnt} -f ${CUTFS} ${fs} >${TMPDIR}/fs 2>${ERR} then ${CAT} ${TMPDIR}/fs > ${fs} else return 1 fi # make sure that EVERY nodename has an entry in the client's /etc/hosts add_host $host || return 1 # add the stanza print "\n${mntpnt}:" >> ${fs} print "\tnodename\t=\t${host}" >> ${fs} print "\tdev\t\t=\t${dir}" >> ${fs} print "\tcheck\t\t=\tfalse" >> ${fs} if [[ -n "${shared_root}" && "${prefix}" = "root" ]] then print "\tvfs\t\t=\tstnfs" >> ${fs} else print "\tvfs\t\t=\tnfs" >> ${fs} fi print "\ttype\t\t=\tdd_boot" >> ${fs} # mount -t used during boot, so set that type here if [[ "${mntpnt}" = "/" ]] || [[ "${mntpnt}" = "/usr" ]] then # if version of the spot is less than 4.2.1 then # don't use the ,llock parm. if [[ ${version} -lt 4 ]] || \ [[ ${version} -eq 4 && ${release} -lt 2 ]] || \ [[ ${version} -eq 4 && ${release} -eq 2 && ${mod} -lt 1 ]] then print "\toptions\t\t=\t${options},hard,intr" >> ${fs} elif [[ ${version} -gt 6 ]] || [[ ${version} -eq 6 && ${release} -ge 1 && ${mod} -ge 2 ]] then # in 530 and above, we need to set acl as an option print "\toptions\t\t=\t${options},hard,intr,llock,acl,nimperf" >> ${fs} elif [[ ${version} -gt 5 ]] || [[ ${version} -eq 5 && ${release} -ge 3 ]] then # in 530 and above, we need to set acl as an option print "\toptions\t\t=\t${options},hard,intr,llock,acl" >> ${fs} else print "\toptions\t\t=\t${options},hard,intr,llock" >> ${fs} fi print "\tmount\t\t=\tautomatic" >> ${fs} else if [[ ${version} -gt 6 ]] || [[ ${version} -eq 6 && ${release} -ge 1 && ${mod} -ge 2 ]] then print "\toptions\t\t=\t${options},bg,nimperf" >> ${fs} else print "\toptions\t\t=\t${options},bg" >> ${fs} fi print "\tmount\t\t=\ttrue" >> ${fs} fi # success return 0 } # add_fs_stanza #*---------------------------- init_filesystems ------------------------------ # # NAME: init_filesystems # # FUNCTION: # initializes the client's /etc/filesystems file # # EXECUTION ENVIRONMENT: # # NOTES: # this function ASSUMES that the current working directory is the client's # root directory # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1 = # 2 = # 3 = # global: # # RETURNS: (int) # 0 = no errors # >0 = failure # # OUTPUT: #-----------------------------------------------------------------------------*/ function init_filesystems { typeset fs="${root}${FILESYSTEMS}" typeset i="" # If we are using a shared root resource, then fill a specific # filesystems file if [[ -n "${shared_root}" ]] then cat ${fs} > ${fs}.${name} fs="${fs}.${name}" fi # add the filesystems stanzas IFS=':' set ${FS_STANZAS} IFS=${OLD_IFS} for i do [[ -z "${i}" ]] && continue # separate mount point from variable name from options set $i # strip the current value for this mount point out of the filesystems file strip_stanzas ${fs} "${1}:" # value specified for this stanza? if eval [[ -z \"\$${2}\" ]] then continue fi add_fs_stanza "${1}" "${2}" "${3}" || err_from_cmd add_fs_stanza done print >> ${fs} # success return 0 } # init_filesystems #*---------------------------- init_swapspaces ------------------------------ # # NAME: init_swapspaces # # FUNCTION: # initializes the client's /etc/swapspaces file # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # root # paging # paging_host # # RETURNS: (int) # 0 = no errors # >0 = failure # # OUTPUT: #-----------------------------------------------------------------------------*/ function init_swapspaces { typeset ss=${root}${SWAPSPACES} paging_host=${paging_host:-${server}} typeset old_root_initialized=${root_initialized} typeset nfs_version="" # If we are using a shared root resource, then fill a specific # swapspaces file. if [[ -n "${shared_root}" ]] then cat ${ss} > ${ss}.${name} ss="${ss}.${name}" # The shared root resource can only be used by diskless # clients who never make use of a local paging space. # So, force the swapspaces file to be stripped each time # init_swapspaces is called, regardless of whether the root # has already been initialized or not. root_initialized= fi # is paging nfs4-exported ? [[ "${paging_vers}" = "4" ]] && nfs_version=":wam" # strip the file if the root hasn't been initialized before [[ -z "${root_initialized}" ]] && strip_stanzas ${ss} "all" # set back original value of root_initialized root_initialized=${old_root_initialized} # strip out current SWAPNFS entry strip_stanzas ${ss} "${SWAPNFS}0:" # if "paging" isn't specified, then this is a dataless machine and the # swapspaces file will be initialized during the boot process # so, skip the rest of the processing which follows [[ -z "${paging}" ]] && return 0 # ASSUMING: remote paging & sequence number is zero print "\n${SWAPNFS}0:" >> ${ss} print "\tdev\t=\t/dev/${SWAPNFS}0" >> ${ss} print "\tremdev\t=\t$paging_host:$paging/${SWAPNFS}0${nfs_version}" >> ${ss} # add another hosts entry if necessary add_host "${paging_host}" || return 1 print >> ${ss} # success return 0 } # end of init_swapspaces #*---------------------------- set_odm_dump -------------------------------- # # NAME: set_odm_dump # # FUNCTION: # Sets the ODM SWservAt attributes related to dump. # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: # # OUTPUT: #-----------------------------------------------------------------------------*/ function set_odm_dump { export ODMDIR=${root}/etc/objrepos odmdelete -q attribute="force_system_dump" -o SWservAt odmdelete -q attribute="tprimary" -o SWservAt odmdelete -q attribute="primary" -o SWservAt odmadd <0 = failure # # OUTPUT: #-----------------------------------------------------------------------------*/ function init_firstboot { typeset fb="${root}${FIRSTBOOT}" typeset dump_host=${dump_host:-${server}} typeset i="" typeset client="" typeset gateway="" typeset snm="" typeset bserver="" typeset rs=${ring_speed:+"-a ring_speed=${ring_speed}"} typeset ct=${cable_type:+"-a cable_type=${cable_type}"} typeset target="" typeset lunid="" typeset port="" typeset boottype="bootp" # If we are using a shared root resource, then fill a specific # firstboot file [[ -n "${shared_root}" ]] && fb="${fb}.${name}" # parse the boot_info set -- ${boot_info} for i do case ${i} in -a*ip=*) # client IP address client=${i##*=} ;; -a*gw=*) # IP address of gateway to BOOTP server gateway=${i##*=} ;; -a*sm=*) # subnetmask snm=${i##*=} ;; -a*sa=*) # BOOTP server IP address bserver=${i##*=} ;; -a*dump_target=*) # dump target target=${i##*=} ;; -a*dump_lunid=*) # dump lunid lunid=${i##*=} ;; -a*dump_port=*) # dump server port port=${i##*=} ;; esac done if [[ -n ${target} && -n ${lunid} && -n ${port} ]] then boottype="iscsi" fi if [[ ${boottype} == "bootp" ]] then # Is this an IPv6 client? Set filename and gw accordingly. if [[ "${client}" = *:* ]] then filename="${hostname}" # Is $gateway a link local address? if echo $gateway | grep -iq ^fe80 then gw="$gateway" else # The firstboot script will set gw_lladdr to the link local # address of the gateway. gw='$gw_lladdr' fi else filename= gw="$gateway" fi # the bootlist command has specific rules about how to set the boot dev list # and init_bootlist_attrs knows how to format it correctly # call it now - info returned via the global variable "bootlist_attrs" init_bootlist_attrs "${client}" "${bserver}" "${gw}" "${filename}" # if adapter seqno not given, ASSUME zero [[ ${adpt_name} != ?*[0-9] ]] && adpt_name=${adpt_name}0 # If iplrom_emu was specified, then just the device information # (rip off /dev) if [[ ${iplrom_emu} != "" ]] then iplrom_emu=${iplrom_emu##*/} fi fi # /etc/firstboot is a shell script print "#!/bin/ksh" > ${fb} # set the bootlist if [[ ${boottype} == "bootp" ]] then # If $gw is a litteral '$gw_lladdr', insert code to set gw_lladdr # to the link local address of the gateway if [[ ${gw} = '$gw_lladdr' ]] then cat <> ${fb} gw_hwaddr=\`/usr/sbin/ndp -n "${gateway}" | ${AWK} '{ print \$5 }'\` gw_lladdr=\`/usr/sbin/ndp -an | ${AWK} -F '[[:space:]()]\+' -v gw_hwaddr="\$gw_hwaddr" '\$5 == gw_hwaddr && substr(\$2,1,4) == "fe80" { print \$2 }'\` EOF fi print "${BOOTLIST} -m normal ${iplrom_emu} ${adpt_name} ${bootlist_attrs}" >> ${fb} else print "hdisk=\$(/usr/lpp/bos.sysmgt/nim/methods/c_disc_target -a operation=discover \ -a target=${target} -a dump_port=${port} -a ipaddr=${bserver} \ -a lun_id=${lunid} )" >> ${fb} print "${BOOTLIST} -m normal \${hdisk}" >> ${fb} fi # Invoke C_MK_NIMCLIENT to configure the network interface using MKTCPIP. # The "-a snm=${snm}" flag is omitted for IPv6. print "${C_MK_NIMCLIENT} -t -a hostname=${hostname} -a ip=${client} \ ${snm:+-a snm=${snm}} ${rs} ${ct}" >> ${fb} # Set the dump device. This is done in /etc/firstboot instead # of in rc.boot or rc.dd_boot because defining /dev/sysdumpnull # when the RAM filesystem is mounted will leave the file # descriptor for /dev/sysdumpnull open in RAM. This would make it # impossible to unmount the RAM filesystem. After the RAM # filesystem is supposedly removed, the file descriptor becomes # invalid. This could cause the machine to crash on the next sync. if [[ ${boottype} == "iscsi" ]] then # We don't want to set the ODM attributes for # shared_root resources, because it has already been # performed during the shared_root creation. if [[ -z "${root_initialized}" && -z "${shared_root}" ]] then set_odm_dump fi elif [[ ${type} == "dataless" ]] then # set the dump device for dataless. There should not # be more than one local swap space defined at this point, # but just in case, always use the first one we find. print "local_swap=\$(grep \"^[^*].*/dev\" /etc/swapspaces | \ sed -e \"/.*\/dev\/swapnfs.*/d; s/.*\(\/dev.*\)/\1/\" | \ awk '{if (NR==1) {print \$1}}')" >> ${fb} print "sysdumpdev -Pp \${local_swap}" >> ${fb} fi # Add the call to set up a nameserver if the resolv_conf resource # was allocated. if [[ -n ${resolv_conf} ]] then if [[ -n ${resolv_conf_host} ]] then echo "${C_CP_RESOLV} -a location=${resolv_conf_host}:${resolv_conf}" >> ${fb} else echo "${C_CP_RESOLV} -a location=${resolv_conf}" >> ${fb} fi fi # # Check to see if we have a license signature file. # if so, sign it with network install. # echo "[[ -f ${LICENSE_SIG} ]] && echo ${license_msg} >> ${LICENSE_SIG}" >> ${fb} # add the dump host add_host ${dump_host} || return 1 # set execute permissions ${CHMOD} 660 ${fb} 2>${ERR} || return 1 # success return 0 } # end of init_firstboot #*---------------------------- inittab ------------------------------ # # NAME: inittab # # FUNCTION: # adds an entry in the clien's /etc/inittab to perform a root sync whenever # it boots # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = no errors # >0 = failure # # OUTPUT: #-----------------------------------------------------------------------------*/ function inittab { typeset itab=${root}${INITTAB} typeset dump_host=${dump_host:-${server}} typeset dumpcheck_itab="" # inittab better be there if [[ ! -s ${itab} ]] then print "/etc/inittab missing: expecting it at ${itab}" >${ERR} return 1 fi # nimclient entry already there? if ${EGREP} "^nimclient:" ${itab} >/dev/null 2>&1 then : else # add an entry right after fbcheck ${AWK} -v new_entry="${NIMCLIENT_ITAB}" \ -v sec_entry="${NIMCLIENT2_ITAB}" \ '/^fbcheck:/ {add=1};{print; if (add){print new_entry;print sec_entry;add=0}}' \ ${itab} >${TMPDIR}/inittab 2>${ERR} || return 1 ${CAT} ${TMPDIR}/inittab >${itab} 2>${ERR} || return 1 fi # If we're using a shared_root resource, add an entry for calling # recfgct right after srcmstr, if not already done ${EGREP} "^recfgct:" ${itab} >/dev/null 2>&1 if [[ ${?} -ne 0 ]] && [[ -n "${shared_root}" ]] then ${AWK} -v new_entry="${RECFGCT_ITAB}" \ '/^srcmstr:/ {add=1};{print; if (add){print new_entry;add=0}}' \ ${itab} >${TMPDIR}/inittab 2>${ERR} || return 1 ${CAT} ${TMPDIR}/inittab >${itab} 2>${ERR} || return 1 fi # success return 0 } # end of inittab #---------------------------- init_rhosts -------------------------------- # # NAME: init_rhosts # # FUNCTION: # initializes the client's /.rhosts file by putting the master's hostname # and UID there # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function init_rhosts { typeset rh=${root}${RHOSTS} # skip if entry already exists [[ -s ${rh} ]] && \ ${EGREP} "^${master}[ ]" ${rh} >/dev/null 2>&1 && \ return 0 # add new entry print "${master} root" >>${rh} 2>${ERR} || return 1 # make sure hostname is resolved add_host ${master} || return 1 return 0 } # end of init_rhosts #---------------------------- init_local_domain -------------------------------- # # NAME: init_local_domain # # FUNCTION: # initializes the client's /etc/nfs/local_domain file # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function init_local_domain { typeset ld=${root}${LOCAL_DOMAIN} # If we are using a shared root resource, then fill a specific # local_domain file [[ -n "${shared_root}" ]] && ld=${ld}.${name} [ ! -d ${ld%/*} ] && mkdir -p ${ld%/*} echo ${nfs_domain} > ${ld} return 0 } # end of init_local_domain