#!/bin/sh # # "@(#)add_install_client.sh 1.122 01/05/07" # # # Copyright (c) 1992-1996 Sun Microsystems, Inc. All Rights Reserved. Sun # considers its source code as an unpublished, proprietary trade secret, and # it is available only under strict license provisions. This copyright # notice is placed here only to protect Sun in the event the source is # deemed a published work. Dissassembly, decompilation, or other means of # reducing the object code to human readable form is prohibited by the # license agreement under which this code is provided to the user or company # in possession of this copy. # # RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the Government # is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the # Rights in Technical Data and Computer Software clause at DFARS 52.227-7013 # and in similar clauses in the FAR and NASA FAR Supplement. # # # Description: # Setup a server for SVR4 client to run install software # # Clients are configured to boot using RARP or DHCP. We can perform # all of the configuration locally for RARP clients, but we can't for # DHCP ones. As such, we tell the user what to do on the DHCP server # for DHCP clients. # # The CDROM has everything needed on the first partition, so there is # no need to create any partitions/hierachies on the server (besides /tftpboot). # # Files (maybe) changed on server: # /tftpboot/ - directory created/populated with inetboot and links. # /etc/ethers - if client Ethernet address isn't known AND not running NIS # /etc/exports || /etc/dfs/dfstab - adds/update export entry for install filesys # /etc/inetd.conf - to turn on tftpboot daemon # /etc/bootparams - adds entry for this client # /etc/hosts - if client IP address not already known to server # make sure path is ok PATH=/usr/bin:/usr/sbin:/sbin:${PATH} # # Constants # SIGHUP=1 SIGINT=2 SIGQUIT=3 SIGTERM=15 # # Variables # CLIENT_NAME="" CLIENT_TYPE="RARP" DHCP_ETHER_ADDR="" DHCP_CLASS_NAME="" PGRP="" PLATFORM_NAME="" BOOT_METHOD="" TmpANS=/tmp/tmpans.$$ # if the LOCKFILE string is changed it MUST also be # changed in rm_install_client # LOCKFILE=/tmp/.install_client.lck LOCKFILE_CREATED= # Functions: # # cleanup_and_exit # # Purpose : Get rid of temporary files and mount points and exit # # Arguments : # exit code # cleanup_and_exit () { if [ -n "$Pkg_admin" -a -f "$Pkg_admin" ]; then rm $Pkg_admin fi if [ -n "$Pkg_mnt" -a -d "$Pkg_mnt" ]; then umount $Pkg_mnt 2> /dev/null rmdir $Pkg_mnt 2> /dev/null fi if [ -n "$LOCKFILE_CREATED" ] ; then rm -f $LOCKFILE fi exit $1 } # # usage # # Purpose : Print the usage message in the event the user # has input illegal command line parameters and # then exit # # Arguments : # none # usage () { echo "Usage: $0 [-i ipaddr] [-e ethernetid] [-s server:path]" echo "\t\t[-c server:path] [-p server:path]" echo "\t\t[-n [name_server]:name_service[(netmask)]]" echo "\t\t[-t install boot image path] client_name platform_group" echo echo "DHCP clients:" echo " $0 -d [-s server:path] [-c server:path]" echo "\t\t[-p server:path] [-t install boot image path]" echo "\t\t[-f boot file name] platform_name platform_group" echo echo " $0 -d -e ethernetid [-s server:path]" echo "\t\t[-c server:path] [-p server:path]" echo "\t\t[-t install boot image path] [-f boot file name]" echo "\t\tplatform_group" cleanup_and_exit 1 } # dirname might not be installed on this (4.X) machine dirname () { expr ${1-.}'/' : '\(/\)[^/]*//*$' \ \| ${1-.}'/' : '\(.*[^/]\)//*[^/][^/]*//*$' \ \| . } # # Find the sources that the nsswitch.conf(4) file defines for the given database # # Takes a single argument, database. # # e.g. - get_sources hosts # # Returns a list of sources on stdout # get_sources() { egrep "^${1}:" /etc/nsswitch.conf 2>/dev/null | \ sed -e '/^$/d' \ -e '/^#/d' \ -e 's/.*://' \ -e 's/\[.*return\].*$//' \ -e 's/\[.*\].*$//' | \ awk '{ for(i=1; i <= NF; i++) { print $i } }' } # # Determine if the given database is served by the given source # # Takes two arguments, database, and service: # # e.g. - db_in_source hosts files # # Returns the status: # 0 - database in listed source. # 1 - database not in listed source. # db_in_source() { echo `get_sources "${1}"` | grep "\<${2}\>" > /dev/null 2>&1 return $? } # # Lookup the given key in the specified database, returns data retrieved, plus # the source that returned it. # # Requires two arguments: database and key # # e.g. - lookup hosts binky # # above example will return data for binky and the source it was found in, if # it is found. # # Has two optional arguments: type_of_lookup and source # # e.g. - lookup hosts 129.152.221.35 byaddr files # # above example will return data for host 129.152.221.35 using an address lookup # in files only. # lookup() { D="${1}" K="${2}" T="${3}" S="${4}" ANS="" status=0 if [ ! -z "${S}" ]; then srcs="${S}" else srcs=`get_sources "${D}"` fi for i in ${srcs}; do SRC=${i} case "${i}" in compat) ;; dns) if [ "${HAVE_DNS}" -ne 0 ]; then NS="DNS" NS_NAME="domain name service" if [ "${D}" = "hosts" ]; then ANS=`nslookup ${K} 2>&1` echo "${ANS}" | grep '^\*\*\*' > /dev/null 2>&1 if [ $? -eq 0 ]; then status=1 else status=0 fi if [ "${status}" -eq 0 ]; then ANS=`echo "${ANS}" | awk '{ if ($1 == "Name:") n=$2 if (n != "") { a=$2 if (substr(a, length(a), 1) == ",") a=substr(a, 1, length(a)-1) } } END{print a, n}'` break fi fi fi ;; files) NS="local" NS_NAME="file" case "${D}" in bootparams) dbfile=/etc/bootparams key="^\<${K}\>" ;; ethers) dbfile=/etc/ethers key="^[ ]*[^# ].*\<${K}\>" [ "${T}" = "byaddr" ] && key="^\<${K}\>" ;; hosts) dbfile=/etc/hosts key="^[ ]*[^# ].*\<${K}\>" [ "${T}" = "byaddr" ] && key="^\<${K}\>" ;; esac if [ -f "${dbfile}" ]; then match_pattern "$K" "${dbfile}" status=$? if [ "${status}" -eq 0 ]; then break fi fi ;; nis) if [ "${HAVE_NIS}" -ne 0 ]; then NS="NIS" NS_NAME="map" case "${D}" in bootparams) mapname=${D} ;; ethers) mapname=${D}.byname [ "${T}" = "byaddr" ] && mapname=${D}.byaddr ;; hosts) mapname=${D}.byname [ "${T}" = "byaddr" ] && mapname=${D}.byaddr [ "${T}" = "byuser" ] && mapname=${D}.byuser ;; esac ANS=`ypmatch "${K}" "${mapname}" 2>/dev/null` status=$? if [ "${status}" -eq 0 ]; then break fi fi ;; nisplus) if [ "${HAVE_NISPLUS}" -ne 0 ]; then NS="NIS+" NS_NAME="table" case "${D}" in bootparams) keyname=name ;; ethers) keyname=name [ "${T}" = "byaddr" ] && keyname=addr ;; hosts) keyname=name [ "${T}" = "bycname" ] && keyname=cname [ "${T}" = "byaddr" ] && keyname=addr ;; esac ANS=`nismatch "${keyname}=${K}" "${D}.org_dir" 2>/dev/null` status=$? if [ "${status}" -eq 0 ]; then break fi fi ;; *) NS="Unknown" NS_NAME="Unknown" SRC="Unknown" ANS="" status=1 ;; esac done ANS=`echo $ANS | sed -e 's/#.*$//'` echo "status=${status}; SRC=${SRC}; ANS=\"${ANS}\"; NS=\"${NS}\"; NS_NAME=\"${NS_NAME}\"" } # # Solaris 2.X system - determine which name service is # being used init_lookup() { HAVE_DNS=0 HAVE_NIS=0 HAVE_NISPLUS=0 # # figure out what options the server has for its database(s) # if [ -f /etc/resolv.conf ]; then # DNS Available for host lookups. HAVE_DNS=1 fi if [ -f /var/nis/NIS_COLD_START ]; then # NIS+_client HAVE_NISPLUS=1 fi if ( ps -ef | grep "[ /]nisd" > /dev/null ); then # NIS+_server HAVE_NISPLUS=1 fi if ( ps -ef | grep "[ /]ypserv" > /dev/null ); then # NIS_server HAVE_NIS=1 fi if ( ps -ef | grep "[ /]ypbind" > /dev/null ); then # NIS_client HAVE_NIS=1 fi } # set the HOST_NAME variable, return status of match get_hostname() { HOST_NAME="" if [ "${Iam}" = "FIVEX" ]; then eval `lookup hosts "${1}" byname "${2}"` elif [ "${Iam}" = "OSX" ]; then HOST_NAME=`lookupd -q host -a name $1 2>/dev/null | egrep '^name:' | awk '{print $2}'` if [ -n "$HOST_NAME" ]; then return 0 fi return 1 else # FOURX if [ "$NS" = "NIS+" ]; then ANS=`nismatch name=${1} hosts.org_dir 2>/dev/null` status=$? elif [ "$NS" = "NIS" ]; then ANS=`ypmatch ${1} hosts 2>/dev/null` status=$? else match_pattern "${1}" "/etc/hosts" status=$? fi fi if [ "$status" -eq 0 ]; then if [ "$NS" = "NIS+" ]; then HOST_NAME=`echo ${ANS} | \ (read cname name addr junk; echo $name)` elif [ "$NS" = "NIS" ]; then HOST_NAME=`echo ${ANS} | \ (read addr name junk; echo $name)` elif [ "$NS" = "DNS" ]; then HOST_NAME=`echo ${ANS} | (read junk name; echo $name)` else HOST_NAME=`echo ${ANS} | \ (read addr name junk; echo $name)` fi fi return $status } # set the HOST_ADDR variable, return status of match get_hostaddr() { HOST_ADDR="" if [ "${Iam}" = "FIVEX" ]; then eval `lookup hosts "${1}" byname "${2}"` elif [ "${Iam}" = "OSX" ]; then HOST_ADDR=`lookupd -q host -a name $1 2>/dev/null | egrep '^ip_address:' | awk '{print $2}'` HOST_ADDR=`expr $HOST_ADDR : '\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)'` if [ ! "${HOST_ADDR}" ] ; then return 1 fi return 0 else # FOURX if [ "$NS" = "NIS+" ]; then ANS=`nismatch name=${1} hosts.org_dir 2>/dev/null` status=$? elif [ "$NS" = "NIS" ]; then ANS=`ypmatch ${1} hosts 2>/dev/null` status=$? else match_pattern "${1}" "/etc/hosts" status=$? fi fi if [ "$status" -eq 0 ]; then if [ "$NS" = "NIS+" ]; then HOST_ADDR=`echo ${ANS} | \ (read cname name addr junk; echo $addr)` elif [ "$NS" = "NIS" ]; then HOST_ADDR=`echo ${ANS} | \ (read addr name junk; echo $addr)` elif [ "$NS" = "DNS" ]; then HOST_ADDR=`echo ${ANS} | (read addr junk; echo $addr)` else HOST_ADDR=`echo ${ANS} | \ (read addr name junk; echo $addr)` fi fi return $status } # set the HOST_ADDR variable, return status of match get_etheraddr() { ETHERNET_ADDR="" if [ "${Iam}" = "FIVEX" ]; then eval `lookup ethers "${1}" byname "${2}"` else # FOURX if [ "$NS" = "NIS+" ]; then ANS=`nismatch name=${1} ethers.org_dir 2>/dev/null` status=$? elif [ "$NS" = "NIS" ]; then ANS=`ypmatch ${1} ethers 2>/dev/null` status=$? else # create a null /etc/ethers if it doesn't exist if [ ! -f /etc/ethers ] ; then cat /dev/null > /etc/ethers fi match_pattern "${1}" "/etc/ethers" status=$? fi fi if [ "$status" -eq 0 ]; then if [ "$NS" = "NIS+" ]; then ETHERNET_ADDR=`echo ${ANS} | \ (read addr name junk; echo $addr)` elif [ "$NS" = "NIS" ]; then ETHERNET_ADDR=`echo ${ANS} | \ (read addr name junk; echo $addr)` else ETHERNET_ADDR=`echo ${ANS} | \ (read addr name junk; echo $addr)` fi fi return $status } # # name_to_ip_addr # # Purpose : Generate a printable IP address from a system name. Intended # for use with user-readable output, so writes out a meaningful # description if the look-up fails. # # Argument : # $1 - the name of the system whose IP address is requested # # Results : # IP address or equivalent written to standard output. # Status returned # name_to_ip_addr() { HOST_ADDR="" get_hostaddr $1 status=$? if [ -z "${HOST_ADDR}" ]; then echo ip-addr-for-$1 else echo ${HOST_ADDR} fi return $status } # return status of match in_bootparams() { if [ "${Iam}" = "FIVEX" ]; then eval `lookup bootparams ${1} byname ${2}` else # FOURX if [ "$NS" = "NIS+" ]; then nismatch ${1} bootparams.org_dir 2>/dev/null status=$? elif [ "$NS" = "NIS" ]; then ypmatch ${1} bootparams 2>/dev/null status=$? else match_pattern "${1}" "/etc/bootparams" status=$? fi fi return $status } # # $1 == pattern to search for # $2 == database to search # match_pattern() { ANS= status=0 Found="no" if [ "${2}" = "/etc/bootparams" ] then key="^\<${1}\>" else key="^[ ]*[^# ].*\<${1}\>" fi grep "${key}" "${2}" 2>/dev/null | while read Line do for KEY in `echo $Line` do if [ "$Found" != "yes" ] ; then if [ "$1" = "$KEY" ] ; then echo $Line > $TmpANS Found="yes" fi fi done done if [ -f $TmpANS ] ; then ANS=`cat ${TmpANS}` rm -f $TmpANS else status=1 fi return $status } # return files, nis or nisplus for the src of a table/map table_src() { if [ "$NS" = "NIS+" ]; then line=`egrep "^${1}:" /etc/nsswitch.conf 2>/dev/null` if [ $? -ne 0 ]; then NS_SRC="files" else NS_SRC=`echo $line | awk '{print $2}'` fi elif [ "$NS" = "NIS" ]; then # ypwhich always exits 0, even if no map exists NS_SRC=`ypwhich -m ${1} 2>&1 | ( read first junk; echo $first )` if [ "${NS_SRC}" = "ypwhich:" ] ; then # no map/server exists NS_SRC="files" else NS_SRC="nis" fi else NS_SRC="files" fi } # # check_network() : # This function splits out the parts of each the netmask($1), the # server ipaddress($2), and the client ipaddress($3). It ANDs each # part of the server ipaddress with the associated part of the # netmask. It also ANDs each part of the client ipaddress with the # associated part of the netmask. The final setp is to determine if # the results of the two ANDs are equal. If they are equal the client # is on the same network interface as the server ipaddress. # # INPUTS: # netmask - netmask of the server network interface, $1 # server ipaddress - network address of the server network interface, $2 # client ipaddress - network address of the client machine $3 # # OUTPUTS: # NETWORK_MATCHED -- 0, not matched # 1, match found # check_network() { # split out the parts of the netmask nm1=`expr $1 : '\(..\)......'` nm2=`expr $1 : '..\(..\)....'` nm3=`expr $1 : '....\(..\)..'` nm4=`expr $1 : '......\(..\)'` # split out the parts of the server ip address SNO1=`expr $2 : '\([0-9]*\)\..*'` SNO2=`expr $2 : '[0-9]*\.\([0-9]*\)\..*'` SNO3=`expr $2 : '[0-9]*\.[0-9]*\.\([0-9]*\)\..*'` SNO4=`expr $2 : '[0-9]*\.[0-9]*\.[0-9]*\.\([0-9]*\)'` # AND the server ipaddress and the netmask SNF1=`perl -e "print(${SNO1}&0x${nm1});"` SNF2=`perl -e "print(${SNO2}&0x${nm2});"` SNF3=`perl -e "print(${SNO3}&0x${nm3});"` SNF4=`perl -e "print(${SNO4}&0x${nm4});"` tmp="${SNF1}.${SNF2}.${SNF3}.${SNF4}" MASKED_SRVR=`echo $tmp | sed 's/ //g'` # split out the parts of the client ip address CNO1=`expr $3 : '\([0-9]*\)\..*'` CNO2=`expr $3 : '[0-9]*\.\([0-9]*\)\..*'` CNO3=`expr $3 : '[0-9]*\.[0-9]*\.\([0-9]*\)\..*'` CNO4=`expr $3 : '[0-9]*\.[0-9]*\.[0-9]*\.\([0-9]*\)'` # AND the client ipaddress and the netmask CNF1=`perl -e "print(${CNO1}&0x${nm1});"` CNF2=`perl -e "print(${CNO2}&0x${nm2});"` CNF3=`perl -e "print(${CNO3}&0x${nm3});"` CNF4=`perl -e "print(${CNO4}&0x${nm4});"` tmp="${CNF1}.${CNF2}.${CNF3}.${CNF4}" MASKED_CLNT=`echo $tmp | sed 's/ //g'` NETWORK_MATCHED=0 if [ ${MASKED_SRVR} = ${MASKED_CLNT} ]; then NETWORK_MATCHED=1 fi } # convert any host alias to real client name get_realname() { get_hostname ${CLIENT_NAME} if [ ! "${HOST_NAME}" ]; then echo "Error: unknown client \"${CLIENT_NAME}\"" cleanup_and_exit 1 fi CLIENT_NAME=${HOST_NAME} } # # tftp_file_name # # Purpose : Determine the name to use for installing a file in ${Bootdir}. # Use an existing file if there is one that matches, otherwise # make up a new name with a version number suffix. # # Arguments : # $1 - the file to be installed # $2 - the prefix to use for forming a name # # Results : # Filename written to standard output. # tftp_file_name() { SRC=$1 BASE=$2 file_to_use= # Determine the name to use for the file in bootdir. # Either use an existing file or make up a new name with a version # number appended. for i in ${Bootdir}/${BASE}* do if cmp -s $SRC $i; then file_to_use=$i break fi done if [ "$file_to_use" ]; then file_to_use=`basename $file_to_use` else # Make this name not a subset of the old style names, so old style # cleanup will work. CONV_GRP=`echo ${PGRP} | tr "[a-z]" "[A-Z]"` max=0 for i in ${Bootdir}/${BASE}.${CONV_GRP}.${VERSION}* ; do max_num=`expr $i : ".*${BASE}.${CONV_GRP}.${VERSION}-\(.*\)"` if [ "$max_num" -gt $max ]; then max=$max_num fi done max=`expr $max + 1` file_to_use=${BASE}.${CONV_GRP}.${VERSION}-${max} fi echo $file_to_use } # # tftp_new_file_size # # Purpose : Determine the amount of extra space required to install a file. # Report 0 if the file already exists, otherwise the size of the # file in units determined by ${Opts_du}. # # Arguments : # $1 - the file to be installed # $2 - the place to install it # # Results : # Size written to standard output. # tftp_new_file_size() { SRC=$1 DEST=$2 if [ -f "$2" ]; then echo 0 else du ${Opts_du} ${SRC} | ( read size name; echo $size ) fi } # # setup_tftp # # Purpose : Create a link from one filename to another. Also store a # command in the cleanup file to remove the created link. # # Arguments : # $1 - the link target # $2 - the link source # setup_tftp() { echo "rm /tftpboot/$1" >> ${CLEAN} if [ -h /tftpboot/$1 ]; then # save it, and stash the cleanup command mv /tftpboot/$1 /tftpboot/$1- echo "mv /tftpboot/$1- /tftpboot/$1" >> ${CLEAN} fi ln -s $2 /tftpboot/$1 } # # Warning message - initial line # warn() { echo "WARNING: $1" } # # Warning message continuation routine # warnc() { echo " $1" } # # warn_export # # Purpose : Warn the user that a filesystem is shared, but is shared with the # wrong permissions. # # Arguments : # none # warn_export() { echo "${EXP_FS} already has an entry in ${EXPORTS}." echo "However, ${EXP_FS} must be ${1} read-only with root access." if [ "X$CLIENT_TYPE" = "XRARP" ] ; then echo "Use ro and either anon=0 or root=${CLIENT_NAME} for ${EXP_FS}." echo "The ${EXPORTS} file must be fixed and ${EXP_FS} ${1}" echo "before ${CLIENT_NAME} can boot." else echo "Use ro and anon=0 for ${EXP_FS}. The ${EXPORTS} file must be" echo "fixed and ${EXP_FS} ${1} before \c" if [ "X$DHCP_CLASS_NAME" = "X" ] ; then echo "${ETHER_ADDR} can boot." else echo "${PLATFORM_NAME} machines can boot." fi fi } # # export_fs # # Purpose : Check to see if directory is exported. If not export it # # Arguments : # $1 - the filesystem to be exported # export_fs() { LAST_EXPORT=$EXP_FS unset EXP_FS FS_TO_EXPORT=$1 # # Set EXP_FS to be the filesystem/directory to use for export # # It may already be exported, if so fine. If not, derive valid # EXP_FS from the current FS_TO_EXPORT directory. If # FS_TO_EXPORT in an exported fs, use that fs. If FS_TO_EXPORT # in an exported subdir, use that subdir. If FS_TO_EXPORT in # an fs with exported subdirs, if FS_TO_EXPORT in one of those # subdirs, use it, otherwise use FS_TO_EXPORT # # first check if the name is already in export file if [ "${Iam}" = "OSX" ]; then NI_FS=`echo $FS_TO_EXPORT | sed 's:/:\\\\/:g'` nicl / cat /exports/$NI_FS name > /dev/null 2>&1 else awk '$0 !~ /^#/ { if ('${EXP_FLD}' == "'${FS_TO_EXPORT}'") {find=1; exit} } END { if (find == 1) exit 0; else exit 1 }' ${EXPORTS} fi if [ $? -eq 0 ]; then # FS_TO_EXPORT already there EXP_FS=$FS_TO_EXPORT if [ "${Iam}" = "OSX" ]; then warn "$EXP_FS is already configured for export in NetInfo. Good Job." warn "I'm not checking any further. The mountd stuff is up to you." return fi else if [ "${Iam}" = "OSX" ]; then warn "I didn't find $EXP_FS configured for export in NetInfo." warn "I'm not checking any further. The mountd stuff is up to you." return fi # FS_TO_EXPORT is not already in export file determine # fs that FS_TO_EXPORT is in assume that by sorting # fs's in reverse order, the first match should be the # filesystem it belongs too. CD_FS=$FS_TO_EXPORT # # Note: there is an 'l' character after the # '$Opts_df}' string. This # is to limit df to local file systems. # for i in `df ${Opts_df}l | grep "^/dev" | awk '{ print $6 }' | sort -r` do if [ "$i" != "$FS_TO_EXPORT" ]; then dir=`echo $i | awk '{ if (length($1) < length(FS_TO_EXPORT)) print substr(FS_TO_EXPORT,1, length($1)) }' FS_TO_EXPORT=$FS_TO_EXPORT` else dir=$FS_TO_EXPORT fi if [ "$i" = "$dir" ]; then CD_FS=$dir break fi done # CD_FS is now set to be the filesystem the CD boot # image is in # if we're in a dir that is already exported, use that for i in `awk '$0 !~ /^#/ {print '${EXP_FLD}'}' ${EXPORTS} | \ egrep "^${CD_FS}"` do # The expr command used to be 'expr # ${FS_TO_EXPORT} : "${i}"', but that fails # when i is /, which is the case if root is # exported. if ( expr ${FS_TO_EXPORT} : "\(${i}\).*" \ > /dev/null 2>&1 ); then EXP_FS=$i break fi done # just use the CD dir if [ -z "${EXP_FS}" ]; then EXP_FS=$FS_TO_EXPORT fi fi #echo "Using $EXP_FS in ${EXPORTS} file" ###################################################################### # now we have gathered all the needed information, configure the server # part. 1 - things that are shared by all clients # # check /etc/exports or /etc/dfs/dfstab for existing export of the CDROM # # grep for CDROM path, if not there, put it there and export it if [ "${Iam}" = "FOURX" ]; then exportfs_needed=0 if grep "^${EXP_FS}[ ]" ${EXPORTS} > /dev/null 2>&1 ; then # EXP_FS already there, see if exported with # ro & root access awk ' { if ($1 != "'${EXP_FS}'") { next } cnt=split($2, args, ",") for (i=1; i<=cnt; i++) { if (substr(args[i], 1, 1) == "-") str=substr(args[i],2,length(args[i])-1) else str=args[i] if (str == "ro") flag++ if (str == "anon=0") flag++ if (substr(str, 1, 5) == "root=") { tmp=substr(str, 6, length(str)-5) rcnt=split(tmp, root, ":") for (j=1; j<=rcnt; j++) if (root[j] == "'${CLIENT_NAME}'") { flag++ } } if (substr(str, 1, 3) == "rw=") { tmp=substr(str, 4, length(str)-3) rcnt=split(tmp, rw, ":") rw_found = 0 for (j=1; j<=rcnt; j++) if (rw[j] == "'${CLIENT_NAME}'") rw_found++ if (rw_found == 0) flag++ } } } END { if (flag >= 2) exit 0 else exit 1 }' ${EXPORTS} # # Only warn the user once that a file system # is not exported correctly. If it has been # done the first time through then don't make # the call again. # if [ $? -ne 0 -a "$EXP_FS" != "$LAST_EXPORT" ]; then warn_export "exported" fi # just tack on a line at the end. else if [ ! -f ${EXPORTS}.orig -a -f "${EXPORTS}" ]; then echo "saving original ${EXPORTS} in" \ "${EXPORTS}.orig" cp ${EXPORTS} ${EXPORTS}.orig fi echo "Adding \"${EXP_FS} -ro,anon=0\"" \ "to ${EXPORTS}" echo "${EXP_FS} -ro,anon=0" >> ${EXPORTS} exportfs_needed=1 fi if [ "$exportfs_needed" -eq 1 ]; then # re-export exportfs -a fi else # FIVEX # Check to see if EXP_FS exists within the EXPORTS # file if so then verify that if is exported with ro & # root access awk ' BEGIN { flag = -1 } { if ($NF != "'${EXP_FS}'") { next } flag = 0 for (f=1; f <= NF; f++) { if (substr($f, 1, 2) == "-o") { if (length($f) == 2) str=$(f+1) else str=substr($f, 3, length($f)-2) cnt=split(str, args, ",") for (i=1; i<=cnt; i++) { if (args[i] == "ro") flag++ if (args[i] == "anon=0") flag++ if (substr(args[i], 1, 5) == "root=") { tmp=substr(args[i], 6, length(args[i])-5) rcnt=split(tmp, root, ":") for (j=1; j<=rcnt; j++) if (root[j] == "'${CLIENT_NAME}'") { flag++ } } } } } } END { if (flag == -1) exit 2 if (flag >= 2) exit 0 else exit 1 }' ${EXPORTS} ret_code=$? # # Only warn the user once that a file system is not # exported correctly. If it has been done the first # time through then don't make the call again. # if [ $ret_code -eq 1 -a "$EXP_FS" != "$LAST_EXPORT" ]; then warn_export "shared" fi if [ $ret_code -eq 2 ]; then if [ ! -f ${EXPORTS}.orig -a -f "${EXPORTS}" ]; then echo "saving original ${EXPORTS} in" \ "${EXPORTS}.orig" cp ${EXPORTS} ${EXPORTS}.orig fi echo "Adding \"share -F nfs -o ro,anon=0 ${EXP_FS}\"" \ "to ${EXPORTS}" echo "share -F nfs -o ro,anon=0 ${EXP_FS}" >> ${EXPORTS} shareall fi fi } # # Install a package from the CD or netinstall path # install_pkg pkg_dir pkg # install_pkg() { if [ "X${PRODUCT_SERVER}" = "X" ]; then Pkg_mnt="" Pkg_dir="${PRODUCT_PATH}/${VERSION}/${1}" else # # Package is on a remote server # Pkg_mnt="/tmp/pkg_install.$$" mkdir $Pkg_mnt mount ${PRODUCT_SERVER}:${PRODUCT_PATH} $Pkg_mnt if [ $? -ne 0 ]; then return $? fi Pkg_dir="${Pkg_mnt}/${VERSION}/${1}" fi # # Attempt to install the package unconditionally # Pkg_admin=/tmp/admin.$$ cat <<- EOF > $Pkg_admin mail= runlevel=nocheck conflict=nocheck setuid=nocheck action=nocheck EOF pkgadd -a $Pkg_admin -d $Pkg_dir $2 > /dev/null 2>&1 Pkg_status=$? rm $Pkg_admin if [ "X${Pkg_mnt}" != "X" ]; then umount $Pkg_mnt rmdir $Pkg_mnt fi return $Pkg_status } abort() { echo "${myname}: Aborted" cleanup_and_exit 1 } # # Do the IP ADDRESS checks # rarp_check_ipaddr() { # if someone gives IP_ADDR and it doesn't match, complain, else # update /etc/hosts if [ "X${IP_ADDR}" != "X" ] ; then get_hostaddr ${CLIENT_NAME} ip_addr=${HOST_ADDR} # check to see if it already exists if [ "${IP_ADDR}" != "${ip_addr}" ]; then echo "Error: Different IP address found in the $NS hosts $NS_NAME" echo " Address for host: ${ip_addr} ${CLIENT_NAME}" cleanup_and_exit 1 fi # if client not in host file, update host file grep "\<${CLIENT_NAME}\>" /etc/hosts >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Adding IP address for ${CLIENT_NAME} to /etc/hosts" echo "${IP_ADDR} ${CLIENT_NAME}" >> /etc/hosts if [ "${Iam}" = "FIVEX" ]; then get_hostaddr ${CLIENT_NAME} if [ $? -ne 0 ]; then db_in_source hosts files if [ $? -ne 0 ]; then warn "IP address for \"${CLIENT_NAME}\" was added to /etc/hosts," warnc "yet nsswitch.conf is not configured to search there." warnc "See nsswitch.conf(4) for additional information." fi fi fi fi fi # if we're not given the address, look it up if [ ! "$IP_ADDR" ]; then get_hostaddr ${CLIENT_NAME} IP_ADDR=${HOST_ADDR} fi # if still don't have it, error if [ ! "${IP_ADDR}" ]; then echo "Error: IP address for $CLIENT_NAME not found in the $NS hosts $NS_NAME" echo " Add it to the $NS hosts $NS_NAME and rerun ${myname}." cleanup_and_exit 1 fi } # # Do the ETHERNET ADDRESS checks # rarp_check_ether() { # if an ethernet address is supplied on the command line, -e 8:0:20:e:a:02, # then do the following: # # - if there is an existing entry (local files, NIS, etc.) and this # entry doesn't match the supplied value, then complain & exit # # - if no existing entry, then update /etc/ethers # if [ "${ETHER_ADDR}" ] ; then # check to see if it already exists get_etheraddr ${CLIENT_NAME} if [ "${ETHERNET_ADDR}" -a "${ETHER_ADDR}" != "${ETHERNET_ADDR}" ]; then echo "Error: Different Ethernet number found in the $NS ethers $NS_NAME" echo " Address for host: ${ETHERNET_ADDR} ${CLIENT_NAME}" cleanup_and_exit 1 fi # if client not in ethers file, update ethers file match_pattern "$CLIENT_NAME" "/etc/ethers" status=$? if [ $status -ne 0 ]; then echo "Adding Ethernet number for ${CLIENT_NAME} to /etc/ethers" echo "${ETHER_ADDR} ${CLIENT_NAME}" >> /etc/ethers if [ "${Iam}" = "FIVEX" ]; then get_etheraddr ${CLIENT_NAME} if [ $? -ne 0 ]; then db_in_source ethers files if [ $? -ne 0 ]; then warn "Ethernet address for \"${CLIENT_NAME}\" was added to /etc/ethers" warnc "yet nsswitch.conf is not configured to search there." warnc "See nsswitch.conf(4) for additional information." fi fi fi fi else # # no ethernet address was supplied. Check to see is an entry exists. # If one does, set ETHERNET_ADDR to that value. # if [ "${Iam}" = "FIVEX" ]; then eval `lookup ethers ${CLIENT_NAME} byname` # if we're not given the ether_addr & using a name service if [ "${NS}" != "local" ]; then # # A map exists. See if we can find an entry. # get_etheraddr ${CLIENT_NAME} if [ ! "${ETHERNET_ADDR}" ] ; then echo "Error: ${CLIENT_NAME} does not exist in the $NS ethers $NS_NAME" echo " Add it, and re-run ${myname}" cleanup_and_exit 1 fi fi else # FOURX # if we're not given the ether_addr & using a name service if [ "${NS}" != "local" ]; then table_src ethers if [ "${NS_SRC}" = "files" ] ; then echo "No $NS ethers $NS_NAME, using /etc/ethers." else # A map exists. The default rarpd won't check /etc/ethers if # YP is running and an ethers map exists. We just have to # tell the user to update the map. get_etheraddr ${CLIENT_NAME} if [ ! "${ETHERNET_ADDR}" ] ; then echo "Error: ${CLIENT_NAME} does not exist in the $NS ethers $NS_NAME" echo " Add it, and re-run ${myname}" cleanup_and_exit 1 fi fi fi fi # either NIS was not running, or there is no ethers map... NO_NIS_OR_PROBLEM= if [ ! "$ETHERNET_ADDR" ]; then status=0 Found="no" ETHERNET_ADDR= grep "^[ ]*[^# ].*\<${CLIENT_NAME}\>" /etc/ethers \ 2>/dev/null | while read Line do for KEY in `echo $Line` do if [ "$Found" != "yes" ] ; then if [ "$CLIENT_NAME" = "$KEY" ] ; then echo $Line | (read addr name ; echo $addr) > $TmpANS Found="yes" fi fi done done if [ -f $TmpANS ] ; then ETHERNET_ADDR=`cat ${TmpANS}` rm -f $TmpANS else status=1 fi NO_NIS_OR_PROBLEM=true fi # if still don't have it, error if [ ! "${ETHERNET_ADDR}" ]; then echo "Error: Ethernet number for $CLIENT_NAME not found." cleanup_and_exit 1 fi # Update ethers file if the client is not in the ethers file AND: # # - NIS is not running or there is no ethers map # or # - This is a FOURX system # match_pattern "${CLIENT_NAME}" "/etc/ethers" status=$? if [ $status -ne 0 ]; then if [ -n "$NO_NIS_OR_PROBLEM" -o "$Iam" = "FOURX" ] ; then echo "Adding Ethernet number for ${CLIENT_NAME} to /etc/ethers" echo "${ETHERNET_ADDR} ${CLIENT_NAME}" >> /etc/ethers if [ "${Iam}" = "FIVEX" ]; then get_etheraddr ${CLIENT_NAME} if [ $? -ne 0 ]; then db_in_source ethers files if [ $? -ne 0 ]; then warn "Ethernet address for \"${CLIENT_NAME}\" was added to /etc/ethers" warnc "yet nsswitch.conf is not configured to search there." warnc "See nsswitch.conf(4) for additional information." fi fi fi fi fi fi } # # start tftpd if needed # start_tftpd() { if [ "${Iam}" = "OSX" ]; then if /sbin/service --test-if-configured-on tftp; then return else /sbin/service tftp start if /sbin/service --test-if-configured-on tftp; then return else echo "Failed to start OSX tftpd via /sbin/service" cleanup_and_exit 1 fi fi fi if grep '^#tftp[ ]' /etc/inetd.conf > /dev/null ; then # found it, so it must be disabled, use our # friend ed to fix it echo "enabling tftp in /etc/inetd.conf" ( echo "/^#tftp/" ; echo "s/#//" ; echo "w"; echo "w"; echo "q" ) | \ ed -s /etc/inetd.conf >/dev/null # but wait, we have to send a HUP to tell it # to re-read inetd.conf pid=`ps ${Opts_ps_pid} | grep '[ /]inetd' | \ ( read pid junk ; echo $pid )` kill -HUP $pid fi # check for correctness of entry in inetd.conf Tftpd=`grep '^tftp[ ]' /etc/inetd.conf | \ ( read svc sock prot wait who path junk ; echo $path ) ` if [ ${Tftpd}"x" = "x" ] ; then echo "tftp entry not found in /etc/inetd.conf" cleanup_and_exit 1 fi if [ ! -x "${Tftpd}" ] ; then echo "tftp program (${Tftpd}) not found or" \ "not executable" cleanup_and_exit 1 fi } # # start the rarp and bootparams servers # start_rarpd_bootparamd() { if (ps ${Opts_ps_pid} | awk '{print $NF}' | \ grep "\<`basename ${Cmd_rarpd}`\>" >/dev/null); then : # found it, no need to start else if [ ! -x "${Cmd_rarpd}" ] ; then echo "${Cmd_rarpd} not found" cleanup_and_exit 1 fi echo "starting rarpd" OLDWD=`pwd` cd / ${Cmd_rarpd} $Opts_rarpd cd ${OLDWD} fi # if this system is 5.x, may need to fix nsswitch.conf for bootparams if [ "${Iam}" = "FIVEX" ]; then srcs=`get_sources bootparams` if [ "${srcs}" != "" ]; then set -- ${srcs} if [ "$1" != "files" ]; then bpent="bootparams: files" for i in ${srcs}; do if [ "$i" != "files" ]; then bpent="${bpent} $i" fi done ed -s /etc/nsswitch.conf <<-EOF /bootparams:/ c ${bpent} . w q EOF echo "changed bootparams entry in /etc/nsswitch.conf" fi else echo "bootparams: files" >> /etc/nsswitch.conf echo "added bootparams entry to /etc/nsswitch.conf" fi fi # rpc.bootparamd - normally started from rc.local or equivalent if ( ps ${Opts_ps} | egrep '[ /]rpc.bootparamd$|[ /]bootparamd$' > /dev/null ) ; then : # found it, no need to start else if [ ! -x "${Cmd_bpd}" ] ; then echo "${Cmd_bpd} not found" cleanup_and_exit 1 fi echo "starting bootparamd" OLDWD=`pwd` cd / ${Cmd_bpd} cd ${OLDWD} fi } # start nfsd and mountd if needed start_nfsd_mountd() { # only started when exports or dfs/dfstab are there if [ "${Iam}" = "OSX" ] ; then if ( ps -x | grep '[0-9][ ]nfsd-master' > /dev/null && \ ps -x | grep '[0-9][ ]nfsd-server' > /dev/null ) ; then : # found it else # didn't find both? kill any nfsd processes laying around... ps -x | grep '[0-9][ ]nfsd-master' | awk '{print $1}' | xargs kill -9 ps -x | grep '[0-9][ ]nfsd-server' | awk '{print $1}' | xargs kill -9 # start it echo "starting nfsd's" /sbin/nfsd -t -u -n 6 fi # kind of redundant to check separately for mountd if ( ps -x | grep '[ /]mountd' > /dev/null ) then : # found it else # start it echo "starting mountd" mountd fi elif [ "${Iam}" = "FOURX" ] ; then if ( ps -x | grep -s '[0-9][ ]*(nfsd)' > /dev/null ) ; then : # found it else # start it - really ought to pay attention to # number in rc.local nfsd 8 & echo "starting nfsd's" fi # kind of redundant to check separately for mountd if ( ps -x | grep '[0-9][ ]*rpc.mountd' > /dev/null ) then : # found it else # start it - using the sequence cribbed from # rc.local echo "starting mountd" if [ -f /etc/security/passwd.adjunct ] ; then # Warning! Turning on port checking # may deny access to older versions # (pre-3.0) of NFS clients. rpc.mountd echo "nfs_portmon/W1" | \ adb -w /vmunix /dev/kmem >/dev/null 2>&1 else rpc.mountd -n fi fi else # Iam FIVEX if ( ps -e | grep '[ /]nfsd$' > /dev/null ) ; then : # found it already else # start it /usr/lib/nfs/nfsd -a 8 & echo "starting nfsd's" fi if ( ps -e | grep '[ /]mountd$' > /dev/null ) ; then : # found it already else # start it /usr/lib/nfs/mountd & echo "starting nfs mountd" fi fi } # # creation of bootparams file entry # # /etc/bootparams should override YP bootparams entry if it is before # the "+" that says go to NIS. # a previous /etc/bootparams entry should supersede a later entry # # However, if a different server responds to the rarp request, the client # will go to that server for bootparam info & get wrong info out of NIS, # thus, ask sysadm to update NIS bootparam map if needed. # configure_bootparams() { if [ "${CONFIG_SERVER}" ]; then add_config="install_config=${CONFIG_SERVER}:${CONFIG_PATH}" fi if [ "${SYSID_CONFIG_SERVER}" ]; then add_sysid_config="sysid_config=${SYSID_CONFIG_SERVER}" add_sysid_config="${add_sysid_config}:${SYSID_CONFIG_PATH}" fi if [ "${BOOT_METHOD}" = "tftp" ] ; then root_opts="rootopts=:rsize=32768" else root_opts="rootopts=:rsize=8192" fi BOOTPARAMS="${CLIENT_NAME} " BOOTPARAMS="${BOOTPARAMS} root=${SUBNET_NAME}:${ROOT}" BOOTPARAMS="${BOOTPARAMS} install=${PRODUCT_SERVER}:${PRODUCT_PATH}" BOOTPARAMS="${BOOTPARAMS} boottype=:in ${add_sysid_config}" BOOTPARAMS="${BOOTPARAMS} ${add_config} ${root_opts} ${NS_POLICY}" if [ "${Iam}" = "OSX" ]; then # check if an entry for this client already exists if nicl / cat /machines/$CLIENT_NAME bootparams 2>/dev/null | grep 'bootparams:' > /dev/null then echo "Warning: ${CLIENT_NAME} has an entry in the $NS bootparams $NS_NAME." echo " Update it with the following entry:" echo ${BOOTPARAMS} echo fi elif [ "${Iam}" = "FIVEX" ]; then eval `lookup bootparams ${CLIENT_NAME}` # check if an entry for this client already in map if [ "${NS}" != "local" ]; then table_src bootparams if [ "${NS_SRC}" != "files" ]; then in_bootparams ${CLIENT_NAME} if [ $? -eq 0 ] ; then echo "Warning: ${CLIENT_NAME} has an entry in the $NS bootparams $NS_NAME." echo " Update it with the following entry:" echo ${BOOTPARAMS} echo fi fi fi else # FOURX # check if an entry for this client already in map if [ "${NS}" != "local" ]; then table_src bootparams if [ "${NS_SRC}" != "files" ]; then in_bootparams ${CLIENT_NAME} if [ $? -eq 0 ] ; then echo "Warning: ${CLIENT_NAME} has an entry in the $NS bootparams $NS_NAME." echo " Update it with the following entry:" echo ${BOOTPARAMS} echo fi fi fi fi # does an old entry already exist? echo "updating /etc/bootparams" if [ "${Iam}" = "OSX" ]; then nicl / delete /machines/$CLIENT_NAME nicl / create /machines/$CLIENT_NAME ip_address $CLIENT_ADDR set $BOOTPARAMS while shift; do if [ -n "$1" ]; then nicl / append /machines/$CLIENT_NAME bootparams $1 fi done return fi grep "^${CLIENT_NAME}[ ]" /etc/bootparams > /dev/null 2>&1 # # Delete any existing ${CLIENT_NAME} entries in an existing # bootparams file. # if [ $? -eq 0 ]; then cp /etc/bootparams /etc/bootparams.orig sed -e "/^${CLIENT_NAME}[ ]/d" /etc/bootparams.orig \ > /etc/bootparams fi # # Add before + (NIS Tag) in file # if [ -s /etc/bootparams ] ; then bootp_last=`tail -1 /etc/bootparams` if [ "+" = "$bootp_last" ]; then ed_cmd=i else ed_cmd=a fi # just tack ours on to end, but before "+" ( echo "/^+" ; echo $ed_cmd ; \ echo "${BOOTPARAMS}" ; echo '.'; echo "w"; echo "q" ) | \ ed /etc/bootparams > /dev/null else # no /etc/bootparams, this is easy echo "${BOOTPARAMS}" >> /etc/bootparams fi } # # MAIN - Program # myname=$0 ID=`id` USER=`expr "${ID}" : 'uid=\([^(]*\).*'` trap abort $SIGHUP $SIGINT $SIGQUIT $SIGTERM # Verify user ID before proceeding - must be root # if [ "${USER}" != "0" ]; then echo "You must be root to run $0" cleanup_and_exit 1 fi # # Lock file to prevent simultaneous access of system files # (/etc/bootparams, /etc/ethers, etc.) # # This is not the best solution, race conditions still exist, # but it is better than nothing. The lock file is created here # and removed in the cleanup_and_exit() function. # if [ ! -f $LOCKFILE ] ; then LOCKFILE_CREATED=yes echo $$ > $LOCKFILE else echo "There is an existing install client lock file, ${LOCKFILE}," echo "which indicates that process `cat $LOCKFILE` is executing" echo "either an add_install_client or a rm_install_client. If this" echo "is not the case, delete the lock file, $LOCKFILE, and " echo "execute $myname again. Otherwise, wait until the lockfile is" echo "removed and re-run $myname." cleanup_and_exit 2 fi # Set up parameters depending on whether this is a # Solaris 1.X (FOURX) or a Solaris 2.X (FIVEX) server # if [ -f /etc/vfstab ]; then Iam="FIVEX"; Opts_df="-k" Opts_du="-sk" Opts_ps="-ef" Opts_ps_pid="-e" Opts_rarpd="-a" Cmd_rarpd="/usr/sbin/in.rarpd" Cmd_bpd="/usr/sbin/rpc.bootparamd" PATH=/sbin:/usr/sbin:/usr/bin:/etc:/etc/nfs SERVER=`uname -n` EXPORTS=/etc/dfs/dfstab EXP_FLD='$NF' elif [ -f /etc/fstab ]; then Iam="FOURX"; Opts_df="" Opts_du="-s" Opts_ps="-ax" Opts_ps_pid="-acx" Opts_rarpd="-a" Cmd_rarpd="/usr/etc/rarpd" Cmd_bpd="/usr/etc/rpc.bootparamd" PATH=/bin:/usr/bin:/usr/ucb:/etc:/usr/etc SERVER=`hostname` EXPORTS=/etc/exports EXP_FLD='$1' elif [ -f /.hidden ]; then Iam="OSX"; Opts_df="-k" Opts_du="-sk" Opts_ps="-ax" Opts_ps_pid="-acx" Opts_rarpd="en0" Cmd_rarpd="/usr/sbin/rarpd" Cmd_bpd="/usr/sbin/bootparamd" PATH=/bin:/usr/bin:/sbin:/usr/sbin SERVER=`hostname` EXP_FLD='$1' else echo "no /etc/fstab or /etc/vfstab, bailing out" cleanup_and_exit 1 fi # export this so shareall can be found export PATH # since NIS_PATH won't be set when client boots, don't use it now unset NIS_PATH # # Parse the command line options. # while [ "$1"x != "x" ]; do case $1 in -i) IP_ADDR="$2"; if [ ! "$IP_ADDR" ]; then usage ; fi # check with expr IP_ADDR=`expr $IP_ADDR : '\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)'` if [ ! "${IP_ADDR}" ] ; then echo "mal-formed IP address: $2" cleanup_and_exit 1 fi shift 2;; -e) ETHER_ADDR="$2"; if [ ! "$ETHER_ADDR" ]; then usage ; fi ETHER_ADDR=`expr $ETHER_ADDR : '\([0-9a-f][0-9a-f]*\:[0-9a-f][0-9a-f]*\:[0-9a-f][0-9a-f]*\:[0-9a-f][0-9a-f]*\:[0-9a-f][0-9a-f]*\:[0-9a-f][0-9a-f]*\)'` if [ ! "${ETHER_ADDR}" ] ; then echo "mal-formed Ethernet address: $2" cleanup_and_exit 1 fi shift 2;; -s) PRODUCT_SERVER=`expr $2 : '\(.*\):.*'` PRODUCT_PATH=`expr $2 : '.*:\(.*\)'` if [ ! "$PRODUCT_SERVER" ]; then usage ; fi if [ ! "$PRODUCT_PATH" ]; then usage ; fi shift 2;; -t) IMAGE_PATH=$2 if [ ! "$IMAGE_PATH" ]; then usage ; fi shift 2;; -D) tools_path=$2 if [ ! "$tools_path" ]; then usage ; fi shift 2;; -d) CLIENT_TYPE="DHCP" shift 1;; -p) SYSID_CONFIG_SERVER=`expr $2 : '\(.*\):.*'` SYSID_CONFIG_PATH=`expr $2 : '.*:\(.*\)'` if [ ! "${SYSID_CONFIG_SERVER}" ]; then usage ; fi if [ ! "${SYSID_CONFIG_PATH}" ]; then usage ; fi shift 2;; -c) CONFIG_SERVER=`expr $2 : '\(.*\):.*'` CONFIG_PATH=`expr $2 : '.*:\(.*\)'` if [ ! "$CONFIG_SERVER" ]; then usage ; fi if [ ! "$CONFIG_PATH" ]; then usage ; fi shift 2;; -n) NS_POLICY=`echo "ns=$2"`; if [ ! "$2" ]; then usage ; fi shift 2;; -f) BOOT_FILE=$2 if [ ! "$BOOT_FILE" ] ; then usage ; fi shift 2;; -*) # -anything else is an unknown argument usage ; ;; [a-zA-Z]*) # one or two trailing words if [ $# -gt 2 -o $# -eq 0 ]; then usage ; fi NAME1="$1"; NAME2="$2"; shift $# ;; *) # then all else is spurious usage ; ;; esac done if [ "${CLIENT_TYPE}" = "DHCP" ] ; then if [ "X$IP_ADDR" != "X" ] ; then echo "ERROR: -i flag cannot be used with DHCP clients" echo usage fi if [ "X$NS_POLICY" != "X" ] ; then echo "ERROR: -n flag cannot be used with DHCP clients" echo usage fi if [ "X$ETHER_ADDR" != "X" -a "X$NAME2" != "X" ] ; then usage fi if [ "X$ETHER_ADDR" = "X" ] ; then # Specified platform name and platform group PLATFORM_NAME="$NAME1"; PGRP="$NAME2"; else # Specified ethernet addr and platform group PLATFORM_NAME=""; PGRP="$NAME1"; fi else # RARP client if [ "X$BOOT_FILE" != "X" ] ; then echo "ERROR: -f flag cannot be used with RARP clients" echo usage fi if [ "X$NAME2" = "X" ] ; then echo "${myname}: No client platform group given." echo usage fi CLIENT_NAME="$NAME1" PGRP="$NAME2" fi # # Get path to the product hierarchy # must maintain the hierarchy structure and RUN THIS COMMAND FROM THE HIERARCHY # # (1) find the path of the hierarchy. # it may be absolute path # it may be some relative path # it may be given in PATH if [ -n "${tools_path}" ]; then TOOLS_DIR=$tools_path else case ${myname} in /*) # absolute path, or found via $PATH (shells turn into abs path) TOOLS_DIR=`dirname ${myname}` myname=`basename ${myname}` ;; ./* | ../*) # relative path from "./" or ../, so we do a bit of clean up TOOLS_DIR=`pwd`/`dirname ${myname}` TOOLS_DIR=`(cd ${TOOLS_DIR} ; pwd )` myname=`basename ${myname}` ;; *) # name found via "." in $PATH TOOLS_DIR=`pwd` ;; esac fi # TOOLS_DIR is now an absolute path to the tools # directory # ## Set the PROD_DIR to the product directory ## This may be either a full netinstall image ## (Product and Tools/Boot directories exist) ## or an install server image (only Tools and ## Tools/Boot exist) # DISTRIBUTION_DIR=`(cd ${TOOLS_DIR}/../.. ; pwd )` SOLARIS_PROD_DIR=`(cd ${TOOLS_DIR}/.. ; pwd )` if [ -n "${IMAGE_PATH}" ]; then if [ ! -d ${IMAGE_PATH} ]; then echo "${myname}: Install boot image ${IMAGE_PATH} does not exist" cleanup_and_exit 1 fi else IMAGE_PATH=${TOOLS_DIR}/Boot fi ## Check to see whether or not the install boot image is in ## the current directory structure if [ -z "${IMAGE_PATH}" ]; then echo "ERROR: Install boot image location could not be determined" echo usage fi # # Verify that IMAGE_PATH is a valid boot image and exists # if [ ! -d ${IMAGE_PATH} ]; then echo "ERROR: Install boot image ${IMAGE_PATH} does not exist" cleanup_and_exit 1 fi # check to see if ${IMAGE_PATH} is a local filesystem # because we cannot export it otherwise. df ${Opts_df} ${IMAGE_PATH} >/dev/null 2>&1 if [ $? -eq 0 ]; then dfout=`df ${Opts_df} ${IMAGE_PATH} | \ ( read junk; read where junk; echo $where )` else MOUNTDIR=`(cd ${IMAGE_PATH}/../../.. ; pwd )` dfout=`df ${Opts_df} | grep ${MOUNTDIR} | (read a junk; echo $a)` fi # # before fix for bugid 4035915 #dfout=`df ${Opts_df} ${IMAGE_PATH} | ( read junk; read where junk; echo $where)` if [ ! -b "${dfout}" ] ; then echo "${myname}: \"${dfout}\" is not a local mount of ${IMAGE_PATH}" echo " cannot export \"${dfout}\" for install clients" cleanup_and_exit 1 fi # # Check to make sure that some critical file exists # if [ ! -d ${IMAGE_PATH}/.tmp_proto ]; then echo "${myname}: ${IMAGE_PATH} is not a valid Solaris install boot image" cleanup_and_exit 1 fi # Set the boot method and boot method particulars # based on the client architecture and client type. # if [ "${PGRP}" = "i86pc" -a "${CLIENT_TYPE}" = "RARP" ]; then # Intel gets it's boot files from the floppy or the CD, so # it doesn't need anything from the OS install server in order # to boot. We do, however, need a place to put the clean up # file. Given that SPARC uses /tftpboot, we'll use that too. BOOT_METHOD="none"; Bootdir="/tftpboot" ExtraSpaceNeeded=0 else BOOT_METHOD="tftp" Bootdir="/tftpboot" Bootprog="tftp" ExtraSpaceNeeded=6 fi # Determine which name service is being used - Solaris 1.X can # only run one name service. Solaris 2.X can run multiple # if [ "${Iam}" = "FOURX" ]; then # # figure out what the server is using for its database(s) # if [ -f /var/nis/NIS_COLD_START ]; then # NIS+_client NS="NIS+" NS_NAME="table" elif ( ps ${Opts_ps} | grep "[ /]nisd" > /dev/null ); then # NIS+_server NS="NIS+" NS_NAME="table" elif ( ps ${Opts_ps} | grep "[ /]ypserv" > /dev/null ); then # NIS_server NS="NIS" NS_NAME="map" elif ( ps ${Opts_ps} | grep "[ /]ypbind" > /dev/null ); then # NIS_client NS="NIS" NS_NAME="map" else NS="local" NS_NAME="file" fi elif [ "${Iam}" = "OSX" ]; then NS="lookupd" NS_NAME="database" else # FIVEX init_lookup fi ############################################################## # locate the correct subnet hostname supported on the local machine. # check the local host table for any entry that matches the ip address # of the clients host name. if [ "X$CLIENT_TYPE" = "XRARP" ] ; then get_realname get_hostaddr ${CLIENT_NAME} CLIENT_ADDR=${HOST_ADDR} if [ "${Iam}" = "OSX" ]; then SERVER_ADDR=`/sbin/ifconfig -a | egrep '^en[0-9]|lo0|inet ' | while read ifname flags ; do read inet ipaddr netmask mask broadcast broadaddr while echo $inet | egrep '^en[0-9]' > /dev/null; do # we found an interface with no 'inet' read inet ipaddr netmask mask broadcast broadaddr done if [ "${ifname}" = "lo0:" ]; then continue; fi mask=\`expr $mask : '0x\(.*\)'\` echo ====== check_network ${mask} ${ipaddr} ${CLIENT_ADDR} ======= check_network ${mask} ${ipaddr} ${CLIENT_ADDR} if [ ${NETWORK_MATCHED} -ne 0 ]; then echo $ipaddr break; fi done` else # Not OSX SERVER_ADDR=`/sbin/ifconfig -a | while read ifname flags ; do if [ "${ifname}" = "ether" ]; then # read ether line from prev interface read ifname flags if [ $? -ne 0 ]; then break fi fi read inet ipaddr netmask mask broadcast broadaddr if [ "${ifname}" = "lo0:" ]; then continue; fi check_network ${mask} ${ipaddr} ${CLIENT_ADDR} if [ ${NETWORK_MATCHED} -ne 0 ]; then echo $ipaddr break; fi done` fi if [ -z "$SERVER_ADDR" ]; then echo "Warning: no interface configured for address $CLIENT_ADDR" SUBNET_NAME=$SERVER else # look for subnet host name in the hosts file SUBNET_NAME=`grep -v "^#" /etc/hosts | grep "^\<$SERVER_ADDR\>" | \ head -1 | awk '{ print $2 }'` if [ -z "$SUBNET_NAME" ]; then echo "Warning: no hostname found for address $SERVER_ADDR" SUBNET_NAME=$SERVER fi fi else SERVER_ADDR=`grep "\<${SERVER}\>" /etc/hosts | \ (read ip nm; echo $ip)` if [ ${SERVER_ADDR}"X" = "X" ] ; then echo "No IP address for $SERVER in /etc/hosts" cleanup_and_exit 1 fi SUBNET_NAME=${SERVER} fi # # If install server only then make sure that the location # of the product directory has been specified # if [ ! -d "${SOLARIS_PROD_DIR}/Product" ] ; then if [ ${PRODUCT_SERVER}"x" = "x" ]; then echo "This system is only set up as a boot server for install clients." echo "Use -s to specify a path to an install server." cleanup_and_exit 1 fi else if [ ${PRODUCT_SERVER}"x" = "x" ]; then PRODUCT_SERVER=${SUBNET_NAME} PRODUCT_PATH=${DISTRIBUTION_DIR} fi fi # VERSION=`basename ${SOLARIS_PROD_DIR}` ROOT=${IMAGE_PATH} USR=${ROOT}/usr # check client kernel and user architecture file trees if [ ! -d "${ROOT}" ] ; then echo "The install root \"${ROOT}\" does not exist" echo "path: ${ROOT}" cleanup_and_exit 1 fi if [ ! -x ${ROOT}/platform/${PGRP} -o ! -x ${USR}/platform/${PGRP} ]; then echo "${myname}: Unsupported platform group." echo usage fi # Process the client-type-specific identification parameters (IP address and # ethernet address for RARP, and platform/class name and ethernet address for # DHCP). if [ "X$CLIENT_TYPE" = "XRARP" ] ; then rarp_check_ipaddr rarp_check_ether else # # Convert the platform name to the DHCP class form # if [ "X${PLATFORM_NAME}" != "X" ] ; then DHCP_CLASS_NAME=`echo "${PLATFORM_NAME}" |sed -e 's/,/./g'` fi # # Convert the Ethernet address to DHCP form # if [ "X${ETHER_ADDR}" != "X" ] ; then DHCP_ETHER_ADDR=`echo "${ETHER_ADDR}" |tr '[a-z]' '[A-Z]' | awk -F: ' { for (i = 1; i <= 6 ; i++) if (length($i) == 1) { printf("0%s", $i) } else { printf("%s", $i); } }'` fi fi # # Add the boot image directory and the products directory (if local) # to the exports file. # if [ "${PRODUCT_SERVER}" = "${SUBNET_NAME}" ]; then export_fs $PRODUCT_PATH fi export_fs $IMAGE_PATH # CLEAN file is used so rm_install_client can undo (most of) the setup if [ "X$CLIENT_TYPE" = "XDHCP" ] ; then if [ "X$DHCP_CLASS_NAME" != "X" ] ; then CLEAN="${Bootdir}/rm.${DHCP_CLASS_NAME}" CLEANUP_FOR="${PLATFORM_NAME}" else CLEAN="${Bootdir}/rm.${DHCP_ETHER_ADDR}" CLEANUP_FOR="${ETHER_ADDR}" fi else CLEAN="${Bootdir}/rm.${IP_ADDR}" CLEANUP_FOR="${CLIENT_NAME}" fi # if config file already exists, run the cleanup - before checking tftpboot if [ -f "${CLEAN}" ] ; then # do the cleanup (of old failed stuff) if [ ! -x ${TOOLS_DIR}/rm_install_client ] ; then echo "WARNING: could not execute: ${TOOLS_DIR}/rm_install_client" echo " cannot clean up preexisting install client \"${CLEANUP_FOR}\"" echo " continuing anyway" else echo "cleaning up preexisting install client \"${CLEANUP_FOR}\"" LOCKFILE_CREATED= rm -f $LOCKFILE ${TOOLS_DIR}/rm_install_client ${CLEANUP_FOR} LOCKFILE_CREATED=yes echo $$ > $LOCKFILE fi fi # # Machines booting via tftp need inetboot files. Determine whether # the one we need is in the boot file area already. If it's not, # determine the size of the one we need. Also determine a unique name # for the one we need. Do the same for the network bootstrap if this # is an i86pc system. # diskneeded=0 if [ "${BOOT_METHOD}" = "tftp" ] ; then aInetboot=${USR}/platform/${PGRP}/lib/fs/nfs/inetboot if [ ! -f ${aInetboot} ]; then aInetboot=${USR}/lib/fs/nfs/inetboot fi Inetboot=`tftp_file_name $aInetboot inetboot` space_needed=`tftp_new_file_size $aInetboot $Inetboot` diskneeded=`expr $diskneeded + ${space_needed} + ${ExtraSpaceNeeded}` if [ "${PGRP}" = "i86pc" ]; then aNbp=${USR}/platform/${PGRP}/boot/solaris/nbp if [ ! -f ${aNbp} ]; then aNbp=${ROOT}/boot/solaris/nbp fi Nbp=`tftp_file_name $aNbp nbp` space_needed=`tftp_new_file_size $aNbp $Nbp` diskneeded=`expr $diskneeded + ${space_needed}` fi fi # # If the user has specified a boot file name, we're going to create a symlink # from the inetboot file to the user specified name. If that link already # exists, make sure it points to the inetboot we're going to use. # if [ "X$BOOT_FILE" != "X" ] ; then if [ -h "${Bootdir}/$BOOT_FILE" -a ! -f "${Bootdir}/$BOOT_FILE" ] ; then echo "ERROR: Specified boot file ${BOOTFILE} already exists, but does not" echo " point to anything. Use the -f option to rm_install_client to" echo " remove it." cleanup_and_exit 1 fi if [ -f "${Bootdir}/$BOOT_FILE" ] ; then cmp -s "${Bootdir}/${Inetboot}" "${Bootdir}/${BOOT_FILE}" if [ $? != 0 ] ; then echo "ERROR: Specified boot file ${BOOT_FILE} already exists," echo " and is of a different version than the one needed for this" echo " client." cleanup_and_exit 1 fi fi fi # # Create the boot file area, if not already created # if [ ! -d "${Bootdir}" ]; then # see if / has the space diskavail=`df ${Opts_df} "/" | tail -1 | awk '{print $4}'` echo "making ${Bootdir}" mkdir ${Bootdir} chmod 775 ${Bootdir} test ${Bootdir} = "/tftpboot" && ln -s . ${Bootdir}/tftpboot else diskavail=`df ${Opts_df} "${Bootdir}" | tail -1 | awk '{print $4}'` fi if [ "${diskneeded}" -gt "${diskavail}" ] ; then echo "Not enough space in/for ${Bootdir}" echo " needed: ${diskneeded}K, have: ${diskavail}K" cleanup_and_exit 1 fi # # Check to see if ETHER address and BOOTPARAMS have been determined # if [ "X$CLIENT_TYPE" != "XDHCP" ] ; then # get server IP address - it has to be in the server /etc/hosts SERVER_IP_ADDR=` grep "\<${SUBNET_NAME}\>" /etc/hosts | \ ( read ip nm; echo $ip ) ` if [ ${SERVER_IP_ADDR}"x" = "x" ] ; then echo "No server ip address for ${SUBNET_NAME} in /etc/hosts" cleanup_and_exit 1 fi fi if [ "${BOOT_METHOD}" = "tftp" ]; then start_tftpd fi # rarpd - normally started from rc.local or equivalent if [ "X$CLIENT_TYPE" = "XRARP" ] ; then start_rarpd_bootparamd fi start_nfsd_mountd ########################################################################## # now we have gathered all the needed information, configure the server # part. 2 - things that are specific to this client if [ "X$CLIENT_TYPE" = "XRARP" ] ; then configure_bootparams fi # # start creating clean up file # echo "#!/sbin/sh" > ${CLEAN} # (re)create it echo "# cleanup file for ${CLEANUP_FOR} - sourced by rm_install_client" \ >> ${CLEAN} # NYD turn off traps ??? probably so # install inet boot program if [ "${BOOT_METHOD}" = "tftp" -a ! -f ${Bootdir}/${Inetboot} ]; then echo "copying inetboot to ${Bootdir}" cp ${aInetboot} ${Bootdir}/${Inetboot} chmod 755 ${Bootdir}/${Inetboot} fi # install PXE network bootstrap program if [ "${PGRP}" = "i86pc" -a "${BOOT_METHOD}" = "tftp" \ -a ! -f ${Bootdir}/${Nbp} ]; then echo "copying nbp to ${Bootdir}" cp ${aNbp} ${Bootdir}/${Nbp} chmod 755 ${Bootdir}/${Nbp} fi if [ "${BOOT_METHOD}" = "tftp" ]; then # Make tftpboot symlinks if [ "X$BOOT_FILE" != "X" ] ; then # Link from the inetboot file to the user-specified name # We don't want to use setup_tftp because we don't want # to save removal commands in the cleanup file ln -s ${Inetboot} ${Bootdir}/$BOOT_FILE cat <<-EOF >>${CLEAN} if [ -h "${Bootdir}/${BOOT_FILE}" -o -f "${Bootdir}/${BOOT_FILE}" ] ; then echo "Not removing manually specified boot file \"${BOOT_FILE}\"" echo "because other clients may be using it. Use the -f flag" echo "to rm_install_client to remove this boot file." else echo "The inetboot file corresponding to the boot file \"${BOOT_FILE}\"" echo "does not exist. Deleting \"${BOOT_FILE}\"." rm -f ${Bootdir}/${BOOT_FILE} fi EOF else if [ "X$CLIENT_TYPE" = "XDHCP" ] ; then # Create links for DHCP clients if [ "X$DHCP_CLASS_NAME" != "X" ] ; then Tftpstring=${DHCP_CLASS_NAME} else Tftpstring=${DHCP_ETHER_ADDR} fi setup_tftp "${Tftpstring}" "${Inetboot}" if [ "${PGRP}" = "i86pc" ]; then NBP_NAME=nbp.${Tftpstring} setup_tftp "${NBP_NAME}" "$Nbp" fi else # Create links for RARP clients HEXIP=`echo $IP_ADDR | awk -F. '{ IP_HEX = sprintf("%0.2x%0.2x%0.2x%0.2x", $1,$2,$3,$4) print IP_HEX }' | tr '[a-z]' '[A-Z]'` HEXIPARCH=${HEXIP}.`echo ${PGRP} | tr '[a-z]' '[A-Z]'` # Some of the very early ss1's may have proms # that do not append the karch when requesting # inetboot. All other sun4c, sun4m and sun4d # machines append karch. IEEE prom based # machines do not append karch. if [ "${PGRP}" = "sun4c" ]; then setup_tftp ${HEXIPARCH} ${Inetboot} setup_tftp ${HEXIP} ${Inetboot} elif [ "${PGRP}" = "sun4m" -o "${PGRP}" = "sun4d" ] ; then setup_tftp ${HEXIPARCH} ${Inetboot} else setup_tftp ${HEXIPARCH} ${Inetboot} setup_tftp ${HEXIP} ${Inetboot} fi fi # # put final clean up commands into clean file # echo 'cnt=`ls -l /tftpboot | grep -w '${Inetboot}' | wc -l `' \ >> ${CLEAN} echo 'if [ ${cnt} -eq 1 ]; then' >> ${CLEAN} echo " echo \"removing /tftpboot/${Inetboot}\"" >> ${CLEAN} echo " rm /tftpboot/${Inetboot}" >> ${CLEAN} echo "fi" >> ${CLEAN} if [ "${PGRP}" = "i86pc" ]; then echo 'cnt=`ls -l /tftpboot | grep -w '${Nbp}' | \ wc -l `' >> ${CLEAN} echo 'if [ ${cnt} -eq 1 ]; then' >> ${CLEAN} echo " echo \"removing /tftpboot/${Nbp}\"" \ >> ${CLEAN} echo " rm /tftpboot/${Nbp}" >> ${CLEAN} echo "fi" >> ${CLEAN} fi fi fi # # If this was a DHCP client, we need to tell the user what to add to the # DHCP server. # if [ "X${CLIENT_TYPE}" = "XDHCP" ] ; then echo if [ "${DHCP_CLASS_NAME}" ] ; then echo "To enable ${PLATFORM_NAME} in the DHCP server," else echo "To enable ${ETHER_ADDR} in the DHCP server," fi echo "add an entry to the server with the following data:" echo echo " Install server (SinstNM) : ${PRODUCT_SERVER}" echo " Install server IP (SinstIP4) :" \ `name_to_ip_addr ${PRODUCT_SERVER}` echo " Install server path (SinstPTH) : ${PRODUCT_PATH}" echo " Root server name (SrootNM) : ${SERVER}" echo " Root server IP (SrootIP4) :" `name_to_ip_addr ${SERVER}` echo " Root server path (SrootPTH) : ${IMAGE_PATH}" if [ "X${BOOT_FILE}" != "X" ] ; then echo " Boot file (BootFile) : ${BOOT_FILE}" elif [ "X${DHCP_ETHER_ADDR}" != "X" ] ; then echo " Boot file (BootFile) : ${DHCP_ETHER_ADDR}" fi if [ "X${CONFIG_SERVER}" != "X" ] ; then echo " Profile location (SjumpsCF) : ${CONFIG_SERVER}:${CONFIG_PATH}" fi if [ "X${SYSID_CONFIG_SERVER}" != "X" ] ; then echo " sysidcfg location (SsysidCF) : ${SYSID_CONFIG_SERVER}:${SYSID_CONFIG_PATH}" fi if [ "${PGRP}" = "i86pc" ]; then echo echo "To enable PXE boot, create a macro definition called" echo "PXEClient:Arch:00000:UNDI:002001 which has the" \ "following values:" echo " Boot file (BootFile) : ${NBP_NAME}" echo " Boot server IP (BootSrvA) :" \ `name_to_ip_addr ${SERVER}` fi fi cleanup_and_exit 0