#!/bin/sh # # ident "@(#)check.sh 1.67 04/05/11 SMI" # # Copyright 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This shell script is for checking the validity of rules, # begin, profiles, finish, and probes. # # NOTE: cannot I18N this script since it runs on 4.x & 5.x systems PATH=/usr/ucb:/usr/sbin/install.d:${PATH} export PATH myname=`basename $0` # check script name for usage message DEBUG=0 TESTING=0 TEST_PROFILE= CONV_TMP_FILE=/tmp/conv_awk.script.$$ PROF_TMP_FILE=/tmp/.profile_names.$$ CMP_VAR=compare_$$ STD_RULES=rules CUSTOM_PROBES=custom_probes # error codes E_PROBE_NOT_FOUND=1 E_CMP_NOT_FOUND=2 E_CLASS_NOT_FOUND=3 E_FINISH_NOT_FOUND=4 E_LACK_ARGS=5 E_BEGIN_NOT_FOUND=6 E_NO_BEGIN_TO_DERIVE=7 E_EXCESS_ARGS=8 E_DUP_INST_TYPE=9 E_INV_INST_TYPE=10 E_DUP_SYS_TYPE=11 E_INV_SYS_TYPE=12 E_DUP_PART_TYPE=13 E_INV_PART_TYPE=14 E_INV_OPTION=15 E_NO_INST_TYPE=16 E_CMP_LACK_ARGS=17 E_BAD_DEVICE_SPEC=18 E_AUTO_EXISTING=19 E_MOUNT_FULL_PATH=20 E_BAD_SIZE=21 E_INVALID_NUMBER=22 E_INVALID_KEY=23 E_NOT_WITH_INITIAL_KEY=24 E_NOT_WITH_FLASH_KEY=25 E_NOT_WITH_UPGRADE_KEY=26 E_BAD_TYPE=27 E_INV_SERVER_ADDR=28 E_DATALESS_EOL=29 E_DUP_ROOT_DEVICE=30 E_INST_TYPE_FIRST=31 E_DUP_BOOT_DEVICE=32 E_INV_PROM_UPDATE=33 E_INV_BACKUP_IDENT=34 E_INV_LAYOUT_CONSTRAINT=35 E_ISABITS=36 E_INV_ABS_PATH=37 E_INV_FL_RETRIEVAL_TYPE=38 E_INV_FL_NFS_HOST_PATH=39 E_INV_FL_NFS_URL=40 E_INV_FL_TAPE_POSN=41 E_INV_FL_HTTP_AUTH_TYPE=42 E_INV_FL_HTTP_TIMEOUT=43 E_INV_FL_HTTP_PROXY=44 E_INV_FL_HTTP_OPTION=45 E_INS_FL_HTTP_AUTH_ARGS=46 E_INS_FL_HTTP_TIMEOUT_ARGS=47 E_INS_FL_HTTP_PROXY_ARGS=48 E_INS_FL_HTTP_BASIC_ARGS=49 E_INV_FL_HTTP_URL=50 E_INS_FL_FTP_TIMEOUT_ARGS=51 E_INS_FL_FTP_PROXY_ARGS=52 E_INV_FL_FTP_TIMEOUT=53 E_INV_FL_FTP_PROXY=54 E_INV_FL_FTP_OPTION=55 E_INV_FL_FTP_URL=56 E_INS_FL_TAPE_BLOCKSIZE_ARGS=57 E_INV_FL_TAPE_BLOCKSIZE=58 E_INV_FL_TAPE_OPTION=59 E_NOT_WITH_FLASH_UPDATE_KEY=60 E_INV_BOOTENV_OPTION=61 E_INV_CREATEBE_OPTION=62 E_INV_CREATEBE_FILESYS=63 E_INV_CREATEBE_ARGS=64 E_INV_CREATEBE_FILESYS_SWAP=65 E_INV_CREATEBE_FILESYS_NOSWAP=66 E_INV_CREATEBE_NO_BENAME=67 E_INV_CREATEBE_NO_FS=68 E_INV_BENAME=69 E_INV_METADB_OPTION=70 E_INV_METADB_ARGS=71 E_BAD_METADB_SIZE=72 E_BAD_METADB_COUNT=73 E_THREE_WAY_MIRROR_NOT_SUPPORTED=74 # fatal_error() # Purpose: print appropriate error according to ${1} # Input: ${1} - error code # fatal_error() { echo "" if [ "${profile_ln}" -a "${profile_ln}" -ne 0 ]; then line_number=${profile_ln} read_file="${profile_file}" else line_number=${ln} read_file="${in_file}" fi echo "Error in file \"${read_file}\", line ${line_number}" echo " ${line}" echo -n "ERROR: " case "${1}" in ${E_EXCESS_ARGS}) echo "Too many arguments for this keyword" ;; ${E_LACK_ARGS}) echo "Insufficient arguments for this keyword" ;; ${E_CMP_LACK_ARGS}) echo "Insufficient arguments for match key: ${cmp_err_rulename}" ;; ${E_PROBE_NOT_FOUND}) echo "Probe function \"probe_${probe_name}\" undefined" ;; ${E_CMP_NOT_FOUND}) echo "Match key: ${key} undefined" ;; ${E_CLASS_NOT_FOUND}) echo "Profile missing: ${profile}" ;; ${E_FINISH_NOT_FOUND}) echo "Finish script missing: ${finish}" ;; ${E_BEGIN_NOT_FOUND}) echo "Begin script missing: ${begin}" ;; ${E_NO_BEGIN_TO_DERIVE}) echo "Missing begin script for derived profile" ;; ${E_DUP_INST_TYPE}) echo "Duplicate install type specified" ;; ${E_INST_TYPE_FIRST}) echo "First specified keyword must be \"install_type\"" ;; ${E_DUP_ROOT_DEVICE}) echo "Duplicate root_device specified" ;; ${E_DUP_BOOT_DEVICE}) echo "Duplicate boot_device specified" ;; ${E_INV_BACKUP_IDENT}) echo "Invalid backup media identifier specified" ;; ${E_INV_LAYOUT_CONSTRAINT}) echo "Invalid layout constraint specified" ;; ${E_INV_INST_TYPE}) echo "Invalid install type specified" ;; ${E_NO_INST_TYPE}) echo "No install type specified" ;; ${E_INV_SERVER_ADDR}) echo "Invalid server address specified" ;; ${E_DUP_SYS_TYPE}) echo "Duplicate system type specified" ;; ${E_INV_SYS_TYPE}) echo "Invalid system type specified" ;; ${E_DUP_PART_TYPE}) echo "Duplicate partitioning specified" ;; ${E_INV_PART_TYPE}) echo "Invalid partitioning specified" ;; ${E_INV_OPTION}) echo "Third argument must be 'add' or 'delete'" ;; ${E_BAD_SIZE}) echo "Size \"${size}\" is invalid" ;; ${E_MOUNT_FULL_PATH}) echo "Mount point \"${mount}\" must begin with a '/'" ;; ${E_AUTO_EXISTING}) echo -n "A \"preserved\" filesystem cannot be \"auto\" sized" echo ", use \"existing\"" ;; ${E_BAD_DEVICE_SPEC}) echo "Device \"${device_name}\" is badly specified" ;; ${E_INVALID_NUMBER}) echo "The argument is not a number" ;; ${E_INVALID_KEY}) echo "Invalid keyword" ;; ${E_NOT_WITH_INITIAL_KEY}) echo "This keyword cannot be used with an install_type" \ "of initial_install" ;; ${E_NOT_WITH_UPGRADE_KEY}) echo "This keyword cannot be used with an install_type" \ "of upgrade" ;; ${E_NOT_WITH_FLASH_KEY}) echo "This keyword cannot be used with an install_type" \ "of flash_install" ;; ${E_BAD_TYPE}) echo "Type \"${type}\" is invalid" ;; ${E_INV_PROM_UPDATE}) echo "Invalid prom update value specified" ;; ${E_X86BOOT_NO_DEVICE}) echo "Fdisk partition type \"x86boot\" requires a device name" ;; ${E_INV_ABS_PATH}) echo "An absolute path must be specified" ;; ${E_INV_FL_RETRIEVAL_TYPE}) echo "Invalid Flash archive retrieval method" ;; ${E_INV_FL_NFS_HOST_PATH}) echo "NFS archive location must be of the form \"host:path\"" ;; ${E_INV_FL_NFS_URL}) echo "NFS URL archive location must be of the form" \ "\"nfs://host/path\"" ;; ${E_INV_FL_TAPE_POSN}) echo "Invalid tape position" ;; ${E_INV_FL_HTTP_AUTH_TYPE}) echo "Invalid HTTP[S] authentication type" ;; ${E_INV_FL_HTTP_TIMEOUT}) echo "HTTP[S] timeout value must be a number" ;; ${E_INV_FL_HTTP_PROXY}) echo "HTTP[S] proxy must be of the form \"host:port\" and \"port\" must be an integer" ;; ${E_INV_FL_HTTP_OPTION}) echo "Invalid HTTP[S] option" ;; ${E_INS_FL_HTTP_AUTH_ARGS}) echo "Insufficient arguments for HTTP[S] authentication option" ;; ${E_INS_FL_HTTP_TIMEOUT_ARGS}) echo "HTTP[S] timeout requires a numeric value" ;; ${E_INS_FL_HTTP_PROXY_ARGS}) echo "HTTP[S] proxy requires an argument" ;; ${E_INS_FL_HTTP_BASIC_ARGS}) echo "Insufficient arguments for HTTP[S] basic authentication" ;; ${E_INV_FL_HTTP_URL}) echo "HTTP[S] URL must be of the form" \ '"http[s]://[:@]/"' ;; ${E_INS_FL_FTP_TIMEOUT_ARGS}) echo "FTP timeout requires a numeric value" ;; ${E_INS_FL_FTP_PROXY_ARGS}) echo "FTP proxy requires an argument" ;; ${E_INV_FL_FTP_TIMEOUT}) echo "FTP timeout value must be a number" ;; ${E_INV_FL_FTP_PROXY}) echo "FTP proxy must be of the form \"host:port\" and \"port\" must be an integer" ;; ${E_INV_FL_FTP_OPTION}) echo "Invalid FTP option" ;; ${E_INV_FL_FTP_URL}) echo "FTP URL must be of the form" \ '"ftp://:@/"' ;; ${E_INS_FL_TAPE_BLOCKSIZE_ARGS}) echo "Insufficient arguments for tape block size" ;; ${E_INV_FL_TAPE_BLOCKSIZE}) echo "Invalid tape blocksize specifier" ;; ${E_INV_FL_TAPE_OPTION}) echo "Invalid tape option" ;; ${E_NOT_WITH_FLASH_UPDATE_KEY}) echo "This keyword cannot be used with an install_type" \ "of flash_update" ;; ${E_INV_BOOTENV_OPTION}) echo "Invalid bootenv operation" ;; ${E_INV_CREATEBE_OPTION}) echo "Invalid Boot Environment creation option" ;; ${E_INV_CREATEBE_FILESYS}) echo "Invalid filesystem specification. Must be " \ "of the form ::" ;; ${E_INV_CREATEBE_ARGS}) echo "Insufficient arguments for createbe option" ;; ${E_INV_CREATEBE_FILESYS_SWAP}) echo "swap filesystem mountpoint must be \"-\"" ;; ${E_INV_CREATEBE_FILESYS_NOSWAP}) echo "mountpoints of \"-\" must be of type \"swap\"" ;; ${E_INV_CREATEBE_NO_BENAME}) echo "Boot Environment must be given a name using \"bename\"" ;; ${E_INV_CREATEBE_NO_FS}) echo "Boot Environment must have at least one filesystem defined" ;; ${E_INV_BENAME}) echo "Invalid BE name (must be at least 1 character, no" \ "more than 30 characters in length)" ;; ${E_INV_METADB_OPTION}) echo "\"$option\" is invalid option. Only size and count " \ "can be options for metadb" ;; ${E_INV_METADB_ARGS}) echo "metadb has incorrect number of arguments" ;; ${E_BAD_METADB_SIZE}) echo "The value for the keyword size \"$size\" is invalid." ;; ${E_BAD_METADB_COUNT}) echo "The value for the keyword count \"$count\" is invalid." ;; ${E_THREE_WAY_MIRROR_NOT_SUPPORTED}) echo "Three way mirror is not supported with filesys keyword." ;; *) echo Internal error - unknown fatal exit code ${1} ;; esac echo exit 1 } # warning_msg() # Purpose: print warning message # Input: ${1} - warning code # warning_msg() { echo "" if [ "${profile_ln}" -a "${profile_ln}" -ne 0 ]; then line_number=${profile_ln} read_file="${profile_file}" else line_number=${ln} read_file="${in_file}" fi echo "Warning in file \"${read_file}\", line ${line_number}" echo " ${line}" echo -n "WARNING: " case "${1}" in ${E_EXCESS_ARGS}) echo "Too many arguments for this keyword" ;; ${E_DATALESS_EOL}) echo "SunSoft plans to remove support for the dataless client system" echo -n " " echo "type after Solaris 2.5. You can use this system type now, but in" echo -n " " echo "future releases you will need to select a different option." ;; esac echo "" } # arg_check() # Purpose: Verify that the specified number of arguments is valid, issuing # a fatal error if not. # Input: ${1} - The number of actual arguments # ${2} - The number of expected arguments # arg_check() { if [ ${1} -gt ${2} ]; then fatal_error ${E_EXCESS_ARGS} elif [ ${1} -lt ${2} ]; then fatal_error ${E_LACK_ARGS} fi } # get_functions() # Purpose: find all the function headers within the file ${1} # get_functions() { while read first rest ; do case ${first} in \#*) # comment continue ;; "") # blank line continue ;; esac # take out comments at end of line, and white space before # comment. this does have the side effect of not allowing # any '#' to be in the grammar, but that is acceptable. tmp_rest=`echo ${rest} | sed 's/[ ]*\#.*$//'` if [ -z "${tmp_rest}" ]; then set -- "" shift else set -- ${first} ${tmp_rest} fi line="${first} ${tmp_rest}" if echo ${line}|grep '[a-zA-Z_][a-zA-Z0-9_]*[ ]*()' >/dev/null then func=`expr "$line" : '\([a-zA-Z_][a-zA-Z0-9_]*\)[ ]*()'` echo ${func} if [ ${DEBUG} -eq 1 ]; then echo ${func} > /dev/tty fi fi done < ${1} } # create_derived_file() # Purpose: create the derived file which will have comments and # blank lines stripped # Input: ${1} - input file name # ${2} - output file name # create_derived_file() { awk -F\# '{print $1}' ${1} | awk 'NF != 0 {print}' \ | sed 's/^[ ]*//g' > ${2} } # append_checksum() # Purpose: append a checksum to the end of the file # Inputs: ${1} - input file name # Returns: 0 - okay # 1 - error # append_checksum() { in_file="${1}" chksum=`/bin/sum ${in_file} | /bin/awk '{print $1}'` echo "# version=2 checksum=${chksum}" >> ${in_file} if [ $? -ne 0 ]; then return 1 fi return 0 } # convert_args # Purpose: this function takes arguments and sees if they have single # quotes around them. If they do, it concatenates all # arguments between each set of "'"'s with _'s instead of spaces. # Inputs: any args # convert_args() { # if no 's then get out if echo $* | grep -v \' > /dev/null 2>&1 then echo $* return fi # because of all the funkiness of handling "'" s, put it in a file if [ ! -f "${CONV_TMP_FILE}" ]; then cat << \FOOBAR > "${CONV_TMP_FILE}" BEGIN { stderr="cat 1>&2" } { i = 1 var="" len=length($0) while (i <= len) { if (substr($0, i, 1) == "'") { if (length(var) > 0) { print var var="" } i++ while (substr($0, i, 1) != "'" && i <= len) { if (substr($0, i, 1) == " ") { var=var "_" } else { var=var substr($0, i, 1) } i++ } if ( i > len ) { print "Mis-matched \"'\" quotes <" $0 ">" | stderr exit 1 } } else { if (length(var) > 0) { print var var="" } while (substr($0, i, 1) != " " && i <= len) { var=var substr($0, i, 1) i++ } } i++ } if (length(var) > 0) print var } FOOBAR fi echo "$*" | nawk -f ${CONV_TMP_FILE} } # check_valid_device() # Purpose: Subroutine to check if a string is a valid device. # A valid device is a string that is either a) a # string that starts with /dev/dsk/ and ends with a # valid slice name, or b) a valid slice name. # Input: ${1} - string to be validated # check_valid_device() { # expr, if passed a single "/" as the first # part of the matching operation, bails since # it thinks it's the division operator. expr "X${1}" : '^X/dev/dsk/' >/dev/null 2>&1 if [ $? != 0 ] ; then # not absolute. must be a slice name is_slice_name "$1" else is_slice_name "`basename $1`" fi } # check_abs_path() # Purpose: Subroutine to check if a string is an absolute path or not. # Absolute paths begin with '/'. This routine does not return # if the path is not absolute. # Input: ${1} - string to be validated # check_abs_path() { # expr, if passed a single "/" as the first # part of the matching operation, bails since # it thinks it's the division operator. expr "X${1}" : '^X/' >/dev/null 2>&1 if [ $? != 0 ] ; then fatal_error ${E_INV_ABS_PATH} fi } # check_part_name() # Purpose: subroutine to check if string is a valid canonical # fdisk partition name # Input: ${1} - string to be validated # check_part_name() { cnt=`expr "${1}" : 'c[0-9][0-9]*t[0-9][0-9]*d[0-9][0-9]*p[1-4]$'` if [ ${cnt} -eq 0 ]; then cnt=`expr "${1}" : 'c[0-9][0-9]*d[0-9][0-9]*p[1-4]$'` if [ ${cnt} -eq 0 ]; then return 0 fi fi return 1 } # check_be_name() # Purpose: subroutine to check if string is a valid BE name # Input: ${1} - string to be validated # check_be_name() { len=`expr "${1}" : '.*'` if [ $? != 0 ] ; then fatal_error ${E_INV_BENAME} fi if [ "${len}" -lt 0 -o "${len}" -gt 30 ]; then fatal_error ${E_INV_BENAME} fi return 1 } # check_slice_name() # Purpose: subroutine to check if string is a valid canonical # slice name # Input: ${1} - string to be validated # check_slice_name() { # expr, if passed a single "/" as the first # part of the matching operation, bails since # it thinks it's the division operator. # For world wide name, we check only for slice. cnt=`expr "X${1}" : 'X.*s[0-9]$'` if [ ${cnt} -eq 0 ]; then cnt=`expr "X${1}" : 'X.*s[0-9]$'` if [ ${cnt} -eq 0 ]; then return 0 fi fi return 1 } # is_slice_name() # Purpose: subroutine determining if string is a valid canonical # slice name # Input: ${1} - string to be validated # is_slice_name() { check_slice_name ${1} if [ $? -eq 0 ]; then device_name=${1} fatal_error ${E_BAD_DEVICE_SPEC} fi } # is_devname() # Purpose: subroutine determining if string is a valid # canonical disk name # Input: ${1} - string to be validated # is_dev_name() { # expr, if passed a single "/" as the first # part of the matching operation, bails since # it thinks it's the division operator. # For world wide name, we don't know anything about disks. # The following check essentially do nothing cnt=`expr "X${1}" : 'X.*[sp0-9]*$'` if [ ${cnt} -eq 0 ]; then cnt=`expr "X${1}" : 'X.*[sp0-9]*$'` if [ ${cnt} -eq 0 ]; then device_name=${1} fatal_error ${E_BAD_DEVICE_SPEC} fi fi } # is_num() # Purpose: Boolean function determining if the string is # comprised entirely of numeric characters # Input: ${1} - string to be validated # is_num() { cnt=`expr "${1}" : '[0-9][0-9]*$'` if [ ${cnt} -eq 0 ]; then return 0 fi return 1 } # is_postfixed_num() # Purpose: Boolean function determining if the string is # comprised entirely of numeric characters, # plus one of an optional postfix. # For example, passing $1="512k" and # $2="k m g" will return true, whereas # "512s" with the same allowable postfixes # will not. # Input: ${1} - string to be validated # ${2} - allowable space-separated postfixes # is_num_postfix() { # if it's just a number, then we're done is_num "${1}" if [ $? -eq 1 ] ; then return 1 fi # check each postfix successively for postfix in $2 ; do cnt=`expr "${1}" : '[0-9][0-9]*'${postfix}'$'` if [ ${cnt} -ne 0 ]; then # we have a match, return TRUE return 1 fi done # no match, return FALSE return 0 } # check_bootenv() # Purpose: check syntax of bootenv keyword in profile # Input: bootenv ... # The number of arguments depends on the bootenv service # check_bootenv() { # convert any quoted tokens into non-quoted tokens, with _ replacing # any spaces. has_bename=no has_fs=no set -- `convert_args $@` case ${1} in createbe) # bootenv createbe bename sourcebe \ # filesystem ... shift 1 while [ $# -gt 0 ] ; do case "${1}" in bename) if [ $# -lt 2 ] ; then fatal_error ${E_INV_CREATEBE_ARGS} fi check_be_name "$2" has_bename=yes shift 2 ;; sourcebe) if [ $# -lt 2 ] ; then fatal_error ${E_INV_CREATEBE_ARGS} fi check_be_name "$2" shift 2 ;; filesystem) if [ $# -lt 2 ] ; then fatal_error ${E_INV_CREATEBE_ARGS} fi # make sure we have three args expr "${2}" : \ '^[^:]*:[^:]*:' >/dev/null 2>&1 if [ $? != 0 ] ; then fatal_error ${E_INV_CREATEBE_FILESYS} fi mntpt=`expr "${2}" : '^\([^:]*\):'` dev=`expr "${2}" : '^[^:]*:\([^:]*\):'` fsopt=`expr "${2}" : '^[^:]*:[^:]*:\(.*\)$'` if [ "$fsopt" = "swap" -a \ "$mntpt" != "-" ] ; then fatal_error ${E_INV_CREATEBE_FILESYS_SWAP} fi if [ "$mntpt" = "-" -a \ "$fsopt" != "swap" ] ; then fatal_error ${E_INV_CREATEBE_FILESYS_NOSWAP} fi if [ "$fsopt" != "swap" ] ; then check_abs_path "$mntpt" fi if [ "$dev" != "shared" ] ; then check_valid_device "$dev" fi has_fs=yes shift 2 ;; *) fatal_error ${E_INV_CREATEBE_OPTION} ;; esac done ;; *) fatal_error ${E_INV_BOOTENV_OPTION} ;; esac if [ "$has_bename" = "no" ] ; then fatal_error ${E_INV_CREATEBE_NO_BENAME} fi if [ "$has_fs" = "no" ] ; then fatal_error ${E_INV_CREATEBE_NO_FS} fi } # check_archive_location() # Purpose: check syntax of archive_location keyword in profile # Input: archive_location ... # ${1} ${2} ... # The number of arguments depends on the retrieval type # check_archive_location() { # convert any quoted tokens into non-quoted tokens, with _ replacing # any spaces. set -- `convert_args $@` case ${1} in nfs) # archive_location nfs : arg_check $# 2 echo "$2" |egrep -s : if [ $? != 0 ] ; then fatal_error ${E_INV_FL_NFS_HOST_PATH} fi ;; nfs://*) # archive_location nfs:// # ( must begin with a /) arg_check $# 1 expr "${1}" : '^nfs://[^/][^/]*/' >/dev/null 2>&1 if [ $? != 0 ] ; then fatal_error ${E_INV_FL_NFS_URL} fi ;; local_tape) # archive_location local_tape [] \ # [blocksize ] if [ $# -lt 2 ] ; then fatal_error ${E_LACK_ARGS} fi # $2, the path, doesn't have to be absolute # If we have only two args, we're done if [ $# -eq 2 ] ; then break fi # if there are exactly 3 arguments, # assume the 3rd is the position. # otherwise, try and parse it along with # the rest of the possible keywords. if [ $# -eq 3 ] ; then is_num ${3} if [ $? -eq 0 ] ; then fatal_error ${E_INV_FL_TAPE_POSN} fi shift 3 else # more than 3 arguments - the third doesn't # have to be the position, it could very well # be blocksize, or something else. is_num ${3} if [ $? -eq 0 ] ; then shift 2 else shift 3 fi fi # parse remaining args. Any we don't recognize, we choke. while [ $# -gt 0 ] ; do case "${1}" in blocksize|bs) if [ $# -lt 2 ] ; then fatal_error \ ${E_INS_FL_TAPE_BLOCKSIZE_ARGS} fi # we allow postfixes of 'k' (kilo) and # 'm' (mega), and (none) when specifying blocksize is_num_postfix "${2}" "k m" if [ $? -eq 0 ] ; then fatal_error ${E_INV_FL_TAPE_BLOCKSIZE} fi shift 2 ;; *) fatal_error ${E_INV_FL_TAPE_OPTION} ;; esac done ;; local_file) # archive_location local_file arg_check $# 2 check_abs_path "${2}" ;; file:/*) # archive_location file:/ arg_check $# 1 # There's nothing to validate beyond what the case # already did. ;; local_device) # archive_location local_device [] if [ $# -lt 3 ] ; then fatal_error ${E_LACK_ARGS} elif [ $# -gt 4 ] ; then fatal_error ${E_EXCESS_ARGS} fi check_abs_path "${2}" check_abs_path "${3}" ;; http|https) # archive_location http[s] \ # [auth basic ] [timeout ] if [ $# -lt 3 ] ; then fatal_error ${E_LACK_ARGS}; fi check_abs_path "${3}" # shift 3 while [ $# -gt 0 ] ; do case "${1}" in auth) if [ $# -lt 2 ] ; then fatal_error ${E_INS_FL_HTTP_AUTH_ARGS} fi shift 1 # Check authentication type case "${1}" in basic) if [ $# -lt 3 ] ; then fatal_error \ ${E_INS_FL_HTTP_BASIC_ARGS} fi shift 3 ;; *) fatal_error ${E_INV_FL_HTTP_AUTH_TYPE} ;; esac ;; timeout) if [ $# -lt 2 ] ; then fatal_error \ ${E_INS_FL_HTTP_TIMEOUT_ARGS} fi is_num "${2}" if [ $? -eq 0 ] ; then fatal_error ${E_INV_FL_HTTP_TIMEOUT} fi shift 2 ;; proxy) if [ $# -lt 1 ] ; then fatal_error \ ${E_INS_FL_HTTP_PROXY_ARGS} fi expr "${2}" : '[^:][^:]*:[0-9][0-9]*$' > /dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_FL_HTTP_PROXY} fi shift 2 ;; *) fatal_error ${E_INV_FL_HTTP_OPTION} ;; esac done ;; http://*|https://*) # archive_location http[s]://[:@]/ \ # [timeout ] # Check url - yes, this expr call has a full path to expr. # The built-ins appear capable of only accepting one \(\) pair # per pattern, which just doesn't work for us. /usr/bin/expr "${1}" : \ '\(https\{0,1\}://\)\([^:][^:]*:[^:@][^:@]*@\)\{0,1\}[^@/][^@/]*/' \ >/dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_FL_HTTP_URL} fi shift 1 while [ $# -gt 0 ] ; do case "${1}" in timeout) if [ $# -lt 2 ] ; then fatal_error \ ${E_INS_FL_HTTP_TIMEOUT_ARGS} fi is_num "${2}" if [ $? -eq 0 ] ; then fatal_error ${E_INV_FL_HTTP_TIMEOUT} fi shift 2 ;; proxy) if [ $# -lt 1 ] ; then fatal_error \ ${E_INS_FL_HTTP_PROXY_ARGS} fi expr "${2}" : '[^:][^:]*:[0-9][0-9]*$' > /dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_FL_HTTP_PROXY} fi shift 2 ;; *) fatal_error ${E_INV_FL_HTTP_OPTION} ;; esac done ;; ftp) # archive_location ftp \ # [timeout ] [proxy :] if [ $# -lt 5 ] ; then fatal_error ${E_LACK_ARGS}; fi check_abs_path "${3}" # shift 5 while [ $# -gt 0 ] ; do case "${1}" in timeout) if [ $# -lt 2 ] ; then fatal_Error \ ${E_INS_FL_FTP_TIMEOUT_ARGS} fi is_num "${2}" if [ $? -eq 0 ] ; then fatal_error ${E_INV_FL_FTP_TIMEOUT} fi shift 2 ;; proxy) if [ $# -lt 1 ] ; then fatal_error \ ${E_INS_FL_FTP_PROXY_ARGS} fi expr "${2}" : '[^:][^:]*:[0-9][0-9]*$' > /dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_FL_FTP_PROXY} fi shift 2 ;; *) fatal_error ${E_INV_FL_FTP_OPTION} ;; esac done ;; ftp://*) # archive_location ftp://:@/ \ # [timeout ] [proxy host:port] # Check url - yes, this expr call has a full path to expr. # The built-ins appear capable of only accepting one \(\) pair # per pattern, which just doesn't work for us. /usr/bin/expr "${1}" : \ '\(ftp://\)\([^:][^:]*:[^:@][^:@]*@\)\{1\}[^@/][^@/]*/' \ >/dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_FL_FTP_URL} fi shift 1 while [ $# -gt 0 ] ; do case "${1}" in timeout) if [ $# -lt 2 ] ; then fatal_error \ ${E_INS_FL_FTP_TIMEOUT_ARGS} fi is_num "${2}" if [ $? -eq 0 ] ; then fatal_error ${E_INV_FL_FTP_TIMEOUT} fi shift 2 ;; proxy) if [ $# -lt 1 ] ; then fatal_error \ ${E_INS_FL_FTP_PROXY_ARGS} fi expr "${2}" : '[^:][^:]*:[0-9][0-9]*$' > /dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_FL_FTP_PROXY} fi shift 2 ;; *) fatal_error ${E_INV_FL_FTP_OPTION} ;; esac done ;; *) fatal_error ${E_INV_FL_RETRIEVAL_TYPE} ;; esac } # check_boot_device() # Purpose: check syntax of boot_device keyword in profile # Input: boot_device # ${1} ${2} # check_boot_device() { # check syntax # valid values: c#[t#]d#s# # c#[t#]d#p# # existing # any check_slice_name ${1} slice=$? check_part_name ${1} part=$? if [ "$slice" -eq 0 -a "$part" -eq 0 -a \ ${1} != "existing" -a ${1} != "any" ]; then device_name=${1} fatal_error ${E_BAD_DEVICE_SPEC} fi # check syntax # valid values: update # preserve if [ "${2}" != "update" -a "${2}" != "preserve" ]; then device_name=${2} fatal_error ${E_INV_PROM_UPDATE} fi } # check_fdisk() # Purpose: check syntax of fdisk keyword in profile # Input: ${1} - # ${2} - # ${3} - # check_fdisk() { # We're doing this backwards (checking field 2 before # checking field 1) because we need `type' to be set # before we can process the size argument. # check syntax # valid values: solaris # dosprimary # x86boot # ### type=`echo ${2} | tr '[A-Z]' '[a-z]'` # lowercase it case "${type}" in solaris | dosprimary | x86boot) ;; *) is_num ${type} if [ $? -eq 0 ]; then cnt=`expr "${type}" : '0x[0-9a-f][0-9a-f]*'` if [ ${cnt} -eq 0 ]; then fatal_error ${E_BAD_TYPE} fi fi ;; esac # check syntax # valid values: rootdisk # all # cx[ty]dz disk=`echo ${1} | tr '[A-z] '[a-z]'` # lowercase it if [ "$type" = "x86boot" ] ; then if [ "$disk"X = "rootdisk"X -o "$disk"X = "all"X ]; then fatal_error ${E_X86BOOT_NO_DEVICE} fi fi if [ "$disk"X != "rootdisk"X -a "$disk"X != "all"X ]; then is_dev_name ${1} fi # check syntax for non-x86boot partitions # valid values: all # delete # maxfree # ### if [ "${type}" != "x86boot" ] ; then size=`echo ${3} | tr '[A-Z]' '[a-z]'` # lowercase it case "${size}" in all | delete | maxfree) ;; *) is_num ${size} if [ $? -eq 0 ]; then fatal_error ${E_BAD_SIZE} fi ;; esac fi } # check_local_filesys() # Purpose: check syntax of local filesys keyword in profile # Input: # filesys [] [] ] [ ] # ${1} ${2} ${3} ${4} ${5} ${6} # # filesys [ [ ] ] # ${1} ${2} [ ${3} ] # # [ preserve [ ] # ${4} ${5} # # [ ] # ${4} # check_local_filesys() { # Check whether mirror is specified. If so extract the mirror # name and use the args differently temp=`echo ${1} | cut -f1 -d':'` if [ "${temp}" = "mirror" ] ; then # Mirror is specified, shift the args disk_param1=$2 check_slice_name $3 if [ $? -eq 0 ]; then size_param=$3 mount_param=$4 disk_param2="" else disk_param2=$3 size_param=$4 mount_param=$5 fi # check syntax # cx[ty]dzs# for disk_param in $disk_param1 $disk_param2 do disk=`echo ${disk_param} | tr '[A-Z]' '[a-z]'` # lowercase it is_slice_name ${disk} done # check syntax # valid values: # free # ### size=`echo ${size_param} | tr '[A-Z]' '[a-z]'` # lowercase it case "${size}" in free) ;; *) # we have a size, so check it out some check_slice_name ${size} if [ $? -ne 0 ]; then fatal_error ${E_THREE_WAY_MIRROR_NOT_SUPPORTED} else # only num is supported is_num ${size} if [ $? -eq 0 ]; then fatal_error ${E_BAD_SIZE} fi fi ;; esac # check syntax # valid values: # mount=`echo ${mount_param} | tr '[A-Z]' '[a-z]'` # lowercase it # expr would take "/" as being a division, # so special case this if [ "$mount" != "swap" ]; then first_let=`expr "${mount_param}"X : '\(.\)[.]*'` if [ "${first_let}" != "/" ]; then fatal_error ${E_MOUNT_FULL_PATH} fi fi else # NO mirror keyword, proceed to check for local filesys disk_param=$1 size_param=$2 mount_param=$3 # check syntax # valid values: "any", # "rootdisk.s"# # :/ # cx[ty]dzs# disk=`echo ${disk_param} | tr '[A-Z]' '[a-z]'` # lowercase it if [ "$disk"X != "anyX" ]; then cnt=`expr $disk : 'rootdisk[.]s[0-7]'` if [ ${cnt} -eq 0 ]; then # must be in c0txdys0 form is_slice_name $disk fi fi # check syntax # valid values: auto # all # existing # free # ###:### # ### size=`echo ${size_param} | tr '[A-Z]' '[a-z]'` # lowercase it case "${size}" in auto | all | existing | free) ;; *) # we have a size, so check it out some # check x:y or just a num dash=`expr ${size} : '[0-9]*\(:\)[0-9]*'` t_start=`expr ${size} : '\([0-9]*\):[0-9]*'` if [ ! "${t_start}" ]; then # if the start cyl is not there, must be a num is_num ${size} if [ $? -eq 0 ]; then fatal_error ${E_BAD_SIZE} fi fi ;; esac # check syntax # valid values: swap # unnamed # overlap # ignore # mount=`echo ${mount_param} | tr '[A-Z]' '[a-z]'` # lowercase it case "${mount}" in swap | unnamed | overlap | ignore) ;; *) if [ -n "${mount_param}" ]; then # expr would take "/" as being a division, # so special case this first_let=`expr "${mount_param}"X : '\(.\)[.]*'` if [ "${first_let}" != "/" ]; then fatal_error ${E_MOUNT_FULL_PATH} fi fi ;; esac fi } # check_remote_filesys() # Purpose: check syntax of remote filesys keyword in profile # Inputs: $* are all the arguments on the remote filesys line # # filesys |"-" [ ] [ ] # ${1} ${2} [ ${3} ] [ ${4} ] # check_remote_filesys() { # check syntax # valid values: - # #.#.#.# if [ "${2}"X != "-"X ]; then cnt=`expr ${2} : '[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*'` if [ ${cnt} -eq 0 ]; then fatal_error ${E_INV_SERVER_ADDR} fi fi # check syntax # valid values: mount=`echo ${3} | tr '[A-Z]' '[a-z]'` # lowercase it first_let=`expr "${3}"X : '\(.\)[.]*'` if [ "${first_let}" != "/" ]; then fatal_error ${E_MOUNT_FULL_PATH} fi } # check_metadb() # Purpose: check syntax of metadb keyword in profile # metadb [size ] [count ] # # Input: ${1} - # ${2} - keyword size or keyword count (optional) # ${3} - or value (optional) # ${4} - keyword size or keyword count (optional) # ${5} - or value (optional) # check_metadb() { # check syntax # valid values: rootdisk # all # cx[ty]dz disk=`echo ${1} | tr '[A-z] '[a-z]'` # lowercase it is_slice_name $disk shift 1 while [ $# -gt 0 ] ; do option=$1 case "${1}" in size) if [ $# -lt 2 ] ; then fatal_error ${E_INV_METADB_ARGS} fi size=`echo ${2} | tr '[A-Z]' '[a-z]'` # lowercase it is_num ${size} if [ $? -eq 0 ]; then fatal_error ${E_BAD_METADB_SIZE} fi shift 2 ;; count) if [ $# -lt 2 ] ; then fatal_error ${E_INV_METADB_ARGS} fi count=`echo ${2} | tr '[A-Z]' '[a-z]'` # lowercase it is_num ${count} if [ $? -eq 0 ]; then fatal_error ${E_BAD_METADB_COUNT} fi shift 2 ;; *) fatal_error ${E_INV_METADB_OPTION} ;; esac done } # validate_location() # Purpose: check syntax of location for package and patch specification # in profile # Input: package ... # ${1} ${2} ... # patch ... # The number of arguments depends on the retrieval type # validate_location() { # Shift three args for package and two for patch # package SUNWpak add ... # patch ... if [ ${1} = "package" ]; then shift 3 elif [ ${1} = "patch" ]; then shift 2 fi # convert any quoted tokens into non-quoted tokens, with _ replacing # any spaces. set -- `convert_args $@` case ${1} in nfs) # archive_location nfs : [ ] if [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} elif [ $# -gt 4 ]; then fatal_error ${E_EXCESS_ARGS} fi echo "$2" |egrep -s : if [ $? != 0 ] ; then fatal_error ${E_INV_NFS_HOST_PATH} fi ;; nfs://*) # archive_location nfs:// [ ] # ( must begin with a /) if [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} elif [ $# -gt 3 ]; then fatal_error ${E_EXCESS_ARGS} fi expr "${1}" : '^nfs://[^/][^/]*/' >/dev/null 2>&1 if [ $? != 0 ] ; then fatal_error ${E_INV_NFS_URL} fi ;; local_file) # local_file arg_check $# 2 check_abs_path "${2}" ;; file:/*) # file:/ arg_check $# 1 # There's nothing to validate beyond what the case # already did. ;; local_device) # archive_location local_device [] if [ $# -lt 3 ] ; then fatal_error ${E_LACK_ARGS} elif [ $# -gt 4 ] ; then fatal_error ${E_EXCESS_ARGS} fi check_abs_path "${3}" ;; http|https) # archive_location http[s] \ # [auth basic ] [timeout ] if [ $# -lt 3 ] ; then fatal_error ${E_LACK_ARGS}; fi check_abs_path "${3}" # shift 3 while [ $# -gt 0 ] ; do case "${1}" in auth) if [ $# -lt 2 ] ; then fatal_error ${E_INS_HTTP_AUTH_ARGS} fi shift 1 # Check authentication type case "${1}" in basic) if [ $# -lt 3 ] ; then fatal_error \ ${E_INS_HTTP_BASIC_ARGS} fi shift 3 ;; *) fatal_error ${E_INV_HTTP_AUTH_TYPE} ;; esac ;; timeout) if [ $# -lt 2 ] ; then fatal_error \ ${E_INS_HTTP_TIMEOUT_ARGS} fi is_num "${2}" if [ $? -eq 0 ] ; then fatal_error ${E_INV_HTTP_TIMEOUT} fi shift 2 ;; proxy) if [ $# -lt 1 ] ; then fatal_error \ ${E_INS_HTTP_PROXY_ARGS} fi expr "${2}" : '[^:][^:]*:[0-9][0-9]*$' > /dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_HTTP_PROXY} fi shift 2 ;; *) fatal_error ${E_INV_HTTP_OPTION} ;; esac done ;; http://*|https://*) # archive_location http[s]://[:@]/ \ # [timeout ] # Check url - yes, this expr call has a full path to expr. # The built-ins appear capable of only accepting one \(\) pair # per pattern, which just doesn't work for us. /usr/bin/expr "${1}" : \ '\(https\{0,1\}://\)\([^:][^:]*:[^:@][^:@]*@\)\{0,1\}[^@/][^@/]*/' \ >/dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_HTTP_URL} fi shift 1 while [ $# -gt 0 ] ; do case "${1}" in timeout) if [ $# -lt 2 ] ; then fatal_error \ ${E_INS_HTTP_TIMEOUT_ARGS} fi is_num "${2}" if [ $? -eq 0 ] ; then fatal_error ${E_INV_HTTP_TIMEOUT} fi shift 2 ;; proxy) if [ $# -lt 1 ] ; then fatal_error \ ${E_INS_HTTP_PROXY_ARGS} fi expr "${2}" : '[^:][^:]*:[0-9][0-9]*$' > /dev/null if [ $? != 0 ] ; then fatal_error ${E_INV_HTTP_PROXY} fi shift 2 ;; *) fatal_error ${E_INV_HTTP_OPTION} ;; esac done ;; *) fatal_error ${E_INV_LOCATION_TYPE} ;; esac } # check_type() # Purpose: make sure the keyword is appropriate for the install_type # specified (fatal exit with error message if not) # Input: ${1} - A combination of 'I', 'U', and/or 'F', specifying # if the command is valid with initial install, Flash # install, or upgrade, respectively. check_type() { type_install=0 type_flash=0 type_upgrade=0 type_flash_update=0 if echo ${1} |egrep -s 'I' ; then type_install=1 fi if echo ${1} |egrep -s 'F' ; then type_flash=1 fi if echo ${1} |egrep -s 'U' ; then type_upgrade=1 fi if echo ${1} |egrep -s 'D' ; then type_flash_update=1 fi if [ "${INSTALL}"X = "INITIAL_INSTALLX" ]; then if [ $type_install -ne 1 ] ; then fatal_error ${E_NOT_WITH_INITIAL_KEY} fi elif [ "${INSTALL}"X = "FLASH_INSTALLX" ] ; then if [ $type_flash -ne 1 ] ; then fatal_error ${E_NOT_WITH_FLASH_KEY} fi elif [ "${INSTALL}"X = "FLASH_UPDATEX" ] ; then if [ $type_flash_update -ne 1 ] ; then fatal_error ${E_NOT_WITH_FLASH_UPDATE_KEY} fi elif [ "${INSTALL}"X = "UPGRADEX" ]; then if [ $type_upgrade -ne 1 ] ; then fatal_error ${E_NOT_WITH_UPGRADE_KEY} fi fi } # deslashify # Purpose:transforms a stream, removing comments # and combining lines that end in a backslash # followed by a newline with the following line. # Input: stdin - the input stream # Output: stdout - the output stream # deslashify() { while line=`/usr/bin/line` ; do # remove comments line=`echo "$line"| sed 's/[ ]*\#.*$//'` # see if it's continued noslash=`expr "$line" : '\(.*\)\\\\$'` if [ $? = 0 ] ; then # found backslash+newline, replace with space # using /usr/bin/echo, which understands \c /usr/bin/echo "${noslash} \c" else # no backslash, do not adulterate it echo "${line}" fi done } # check_profile_file() # Purpose: syntax check the profile # Input: ${1} - input file name # check_profile_file() { profile_ln=0 INSTALL="" ROOTDEV="" BOOTDEV="" SYSTEM="" PRODUCT="" PARTITION="" META="" while read first rest ; do profile_ln=`expr ${profile_ln} + 1` case ${first} in \#*) # comment continue ;; "") # blank line continue ;; esac # take out comments at end of line, and white space before # comment. this does have the side effect of not allowing # any '#' to be in the grammar, but that is acceptable. tmp_rest=`echo ${rest} | sed 's/[ ]*\#.*$//'` if [ -z "${tmp_rest}" ]; then set -- "" shift else set -- ${tmp_rest} fi # set current line being evaluated for error reporting line="${first} ${tmp_rest}" keyword=`echo ${first} | tr '[a-z]' '[A-Z]'` # uppercase it # process the keyword case ${keyword} in ARCHIVE_LOCATION) # we need at least one argument if [ $# -lt 1 ] ; then fatal_error ${E_LACK_ARGS} fi check_type "FD" check_archive_location "$@" ;; BOOTENV) # we need at least one argument if [ $# -lt 1 ] ; then fatal_error ${E_LACK_ARGS} fi check_type "FI" check_bootenv "$@" ;; LOCAL_CUSTOMIZATION) # we need at least one argument if [ $# -lt 1 ] ; then fatal_error ${E_LACK_ARGS} fi check_type "FD" ;; BACKUP_MEDIA) # validate argument count if [ $# -gt 2 ]; then fatal_error ${E_EXCESS_ARGS} elif [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi # uppercase the identifier IDENTIFIER=`echo ${1} | tr '[a-z]' '[A-Z]'` case ${IDENTIFIER} in LOCAL_TAPE | LOCAL_DISKETTE | \ LOCAL_FILESYSTEM | REMOTE_SYSTEM | \ REMOTE_FILESYSTEM) ;; *) fatal_error ${E_INV_BACKUP_IDENT} ;; esac check_type "UD" ;; CLIENT_ARCH) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi check_type "I" ;; CLIENT_SWAP) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi is_num ${1} if [ $? -eq 0 ]; then fatal_error ${E_INVALID_NUMBER} fi check_type "I" ;; CLIENT_ROOT) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi is_num ${1} if [ $? -eq 0 ]; then fatal_error ${E_INVALID_NUMBER} fi check_type "I" ;; CLUSTER) # validate argument count if [ $# -gt 2 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} elif [ $# -eq 2 ]; then # uppercase the action field ACTION=`echo ${2} | tr '[a-z]' '[A-Z]'` if [ "${ACTION}" != "ADD" -a \ "${ACTION}" != "DELETE" ]; then fatal_error ${E_INV_OPTION} echo $? fi fi check_type "IU" ;; DONTUSE) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi is_dev_name ${1} check_type "IF" ;; FDISK) # validate arguments if [ $# -gt 3 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 3 -a $2 != "x86boot" ]; then fatal_error ${E_LACK_ARGS} fi check_fdisk $* check_type "IF" ;; FILESYS) # validate arguments if [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi cnt=`expr ${1} : '..*:/..*'` # Remote File System if [ ${cnt} -gt 0 ]; then if [ $# -gt 4 ]; then fatal_error ${E_EXCESS_ARGS} elif [ $# -lt 3 ]; then fatal_error ${E_LACK_ARGS} fi check_remote_filesys $* # Local File System else # validate argument count if [ $# -gt 7 ]; then fatal_error ${E_EXCESS_ARGS} elif [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi check_local_filesys $* fi check_type "IF" ;; METADB) # validate arguments if [ $# -gt 5 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi check_metadb $* check_type "IF" ;; INSTALL_TYPE) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi # check for duplicate specification if [ "${INSTALL}X" != "X" ]; then fatal_error ${E_DUP_INST_TYPE} fi # check for valid value INSTALL=`echo ${1} | tr '[a-z]' '[A-Z]'` if [ "${INSTALL}" != "INITIAL_INSTALL" -a \ "${INSTALL}" != "FLASH_INSTALL" -a \ "${INSTALL}" != "FLASH_UPDATE" -a \ "${INSTALL}" != "UPGRADE" ]; then fatal_error ${E_INV_INST_TYPE} fi ;; LAYOUT_CONSTRAINT) # validate argument count if [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi # uppercase the identifier IDENTIFIER=`echo ${2} | tr '[a-z]' '[A-Z]'` case ${IDENTIFIER} in MOVABLE | AVAILABLE | COLLAPSE) if [ $# -gt 2 ]; then fatal_error ${E_EXCESS_ARGS} fi ;; CHANGEABLE) if [ $# -eq 3 ]; then is_num ${3} if [ $? -eq 0 ]; then fatal_error ${E_INVALID_NUMBER} fi elif [ $# -gt 3 ]; then fatal_error ${E_EXCESS_ARGS} fi ;; *) fatal_error ${E_INV_LAYOUT_CONSTRAINT} ;; esac is_slice_name ${1} check_type "U" ;; LOCALE) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi check_type "IU" ;; GEO) # validate argument count if [ $# -gt 1 ] ; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ] ; then fatal_error ${E_LACK_ARGS} fi check_type "IU" ;; NOREBOOT) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "IF" ;; NORECONFIGURE) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "IF" ;; NOTRANSFER) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "IF" ;; NODISKOPS) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "IF" ;; NO_MASTER_CHECK) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "D" ;; NO_CONTENT_CHECK) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "D" ;; FORCED_DEPLOYMENT) # validate argument count if [ $# -gt 0 ]; then warning_msg ${E_EXCESS_ARGS} fi check_type "D" ;; NUM_CLIENTS) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi is_num ${1} if [ $? -eq 0 ]; then fatal_error ${E_INVALID_NUMBER} fi check_type "I" ;; PACKAGE) # validate argument count if [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} elif [ $# -ge 2 ]; then # uppercase the action field ACTION=`echo ${2} | tr '[a-z]' '[A-Z]'` if [ "${ACTION}" != "ADD" -a \ "${ACTION}" != "DELETE" ]; then fatal_error ${E_INV_OPTION} fi fi if [ $# -gt 2 ]; then validate_location "package" $* fi ;; PARTITIONING) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi # check for valid value PARTITION=`echo ${1} | tr '[a-z]' '[A-Z]'` if [ "${PARTITION}" != "DEFAULT" -a \ "${PARTITION}" != "EXISTING" -a \ "${PARTITION}" != "EXPLICIT" ]; then fatal_error ${E_INV_PART_TYPE} fi check_type "IF" ;; PATCH) # validate argument count if [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi validate_location "patch" $* ;; SWAP_SIZE) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi is_nums ${1} if [ $? -eq 0 ]; then fatal_error ${E_BAD_SIZE} fi check_type "IF" ;; SYSTEM_TYPE) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi # check for duplicate system_type if [ "${SYSTEM}X" != "X" ]; then fatal_error ${E_DUP_SYS_TYPE} fi # check for valid value SYSTEM=`echo ${1} | tr '[a-z]' '[A-Z]'` if [ "${SYSTEM}" = "DATALESS" ]; then warning_msg ${E_DATALESS_EOL}; elif [ "${SYSTEM}" != "STANDALONE" -a \ "${SYSTEM}" != "SERVER" ]; then fatal_error ${E_INV_SYS_TYPE} fi check_type "I" ;; USEDISK) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi is_dev_name ${1} check_type "IF" ;; ROOT_DEVICE) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi # check for duplicate root_device if [ "${ROOTDEV}X" != "X" ]; then fatal_error ${E_DUP_ROOT_DEVICE} fi is_slice_name ${1} ROOTDEV=${1} ;; BOOT_DEVICE) # validate argument count if [ $# -gt 2 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi # check for duplicate boot_device if [ "${BOOTDEV}X" != "X" ]; then fatal_error ${E_DUP_BOOT_DEVICE} fi check_boot_device $* check_type "IF" BOOTDEV=${1} ;; ISA_BITS) # validate argument count if [ $# -gt 1 ]; then warning_msg ${E_EXCESS_ARGS} elif [ $# -lt 1 ]; then fatal_error ${E_LACK_ARGS} fi check_type "IU" ;; *) # default fatal_error ${E_INVALID_KEY} ;; esac # check for install_type first if [ "${INSTALL}X" = "X" ]; then fatal_error ${E_INST_TYPE_FIRST} fi done # fix the variable line up for any possible warning messages. line="profile ${profile}" if [ "${INSTALL}X" = "X" ];then fatal_error ${E_NO_INST_TYPE} fi profile_ln=0 } # check_rule_file() # Purpose: check syntax of input file (rules) and create derived # file # Input: ${1} - input file name # check_rule_file() { # remove any previously existing profile list file echo > ${PROF_TMP_FILE} # read each rule one at a time, since we don't have to worry about # being in a subshell of the main shell here. in_file="${1}" ln=0 while read first rest; do ln=`expr ${ln} + 1` case ${first} in \#*) # comment continue ;; "") # blank line continue ;; esac # take out comments at end of line this does have the side # effect of not allowing any '#' to be in the grammar, but # that is acceptable. rule=`echo ${first} ${rest} | sed 's/\#.*$//'` # now, for each rule that we have to parse, break it up into # individual match and multiple match_key arguments. set -- ${rule} line="${rule}" # we need at least 2 args on a probe line... if [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi # if probe function, then run it if [ ${1}"X" = "probeX" ]; then probe_name=${2} # find the probe function in ${CHECK_PROBE} or # ${CUSTOM_PROBES} we can't force the { to be on the # same line, unfortunately grep "probe_${probe_name}[ ]*()" ${CHECK_PROBE} \ >/dev/null rc1=$? # if we found it already, don't look anymore if [ $rc1 -ne 0 ]; then if [ "${CUSTOM_PROBES}" ]; then grep "probe_${probe_name}[ ]*()" \ ${CUSTOM_PROBES} >/dev/null 2>&1 rc2=$? else rc2=1 fi else rc2=0 fi if [ $rc1 -ne 0 -a $rc2 -ne 0 ]; then fatal_error ${E_PROBE_NOT_FOUND} fi continue fi if [ $# -lt 2 ]; then fatal_error ${E_LACK_ARGS} fi # not a probe...so it must be a compare. # grab the begin, profile, and end scripts/profiles eval `echo ${rule} | \ awk '{printf( "begin=%s; profile=%s; finish=%s", \ $(NF - 2), $(NF - 1), $NF)}'` if [ ${DEBUG} -eq 1 ]; then echo begin=${begin} profile=${profile} finish=${finish} fi # strip the line of the last three arguments... # ("begin", "profile", and "end").. to give only # match/match_key section of the rule matches=`echo ${rule} | \ awk '{ for (i = 1; i < NF - 2; i++) printf("%s ",$i) }'` set -- ${matches} if [ $# -lt 2 ]; then cmp_err_rulename=${1} fatal_error ${E_CMP_LACK_ARGS} fi # split the matches into separate variables, # independent of # of args for easy parsing... ... # it might seem gorpy, but it simplifies the # handling the arguments and leaves the compare # routines free to do what they will with the # arguments. eval `echo ${matches} | awk -F'&&' ' BEGIN {count = 0} { for (i = 1; i <= NF; i++) if ($i != "") { count++ cmd_var = var"_"count printf("%s=\"%s\";",cmd_var,$i) } } END {printf("tot_cmps=%s", count)}' \ var=${CMP_VAR}` # because of previous checks, we must have at least 1 # compare by the time we get here (good syntax is unknown yet) saverc=1 # start with no match cmp_num=0 # make all the compare matches. # NOTE: this loop is running in a subshell, so make sure # you check the exit status of this subshell (after # the loop completes) to determine the exit status # of the main shell while test ${cmp_num} -lt ${tot_cmps} ; do not_flag=0 cmp_num=`expr ${cmp_num} + 1` comp=`eval echo $"${CMP_VAR}_${cmp_num}"` set -- ${comp} if [ "${1}"X = "!"X ]; then not_flag=1 shift fi # check should have made sure there are at least 2 # arguments. set -- `convert_args $*` key=${1} first_arg=${2} # basic check for multi-arg rule cnt if [ "${key}" = "disksize" ] ; then arg_check $# 3 elif [ "${key}" = "installed" ] ; then arg_check $# 3 fi # run the comparison function; find the probe function # in ${CHECK_PROBE} can't force the { to be on the same # line, unfortunately grep "cmp_${key}[ ]*()" ${CHECK_PROBE} > /dev/null rc1=$? # if we found it already, don't look anymore if [ $rc1 -ne 0 ]; then if [ "${CUSTOM_PROBES}" ]; then grep "cmp_${key}[ ]*()" \ ${CUSTOM_PROBES} > /dev/null rc2=$? else rc2=1 fi else rc2=0 fi if [ $rc1 -ne 0 -a $rc2 -ne 0 ]; then fatal_error ${E_CMP_NOT_FOUND} fi done # if we get here... then all we have left to check for is # the begin, profile, and finish scripts if [ "${begin}" != "-" -a ! -f "${begin}" ]; then fatal_error ${E_BEGIN_NOT_FOUND} fi # profile type case "${profile}" in "-") # null ;; "--") # suppress Solstart - treat as null ;; "=") # derived, must have begin if [ ! -f ${begin} ]; then fatal_error ${E_NO_BEGIN_TO_DERIVE} fi ;; *) # a file name if [ ! -f ${profile} ]; then fatal_error ${E_CLASS_NOT_FOUND} fi ;; esac if [ "${finish}" != "-" -a ! -f ${finish} ]; then fatal_error ${E_FINISH_NOT_FOUND} fi # check input file using check_input file # a "-" means no profile, so don't check it if [ "${profile}" != "-" -a "${profile}" != "--" \ -a "${profile}" != "=" ]; then # only check the profile if it hasn't been # checked before; look for an exact match exist=`egrep -c "^${profile}$" ${PROF_TMP_FILE}` if [ ${exist} -eq 0 ]; then echo "Validating profile ${profile}..." echo ${profile} >> ${PROF_TMP_FILE} profile_file="${profile}" export profile_file cat ${profile} 2> /dev/null | \ deslashify 2> /dev/null | \ check_profile_file || exit $? fi fi done < ${in_file} estatus=$? rm -f ${PROF_TMP_FILE} if [ ${estatus} -ne 0 ]; then exit 1 fi } # # ok_check_perms() # Purpose: check perms on .ok files # Input: ${1} - required permissions # ${2} - file to check perms of # ok_check_perms() { ocp_perms=${1} ocp_file=${2} # change to correct perms if chmod a+${ocp_perms} ${ocp_file} > /dev/null 2>&1; then return fi # can't set perms, so check them.... perl would be nice here. # if the appropriate r and x permissions exist, don't fail # the check case ${ocp_perms} in "r") ocp_mask='cbplstwx-' ocp_string='rrr' ;; "rx") ocp_mask='cbplstw-' ocp_string='rxrxrx' ;; esac ocp_p=`/bin/ls -l ${ocp_file} | nawk '{print $1}' | \ sed 's/['${ocp_mask}']//g'` # if appropriate permissions already exist, then don't complain to # user if [ ${ocp_string} != ${ocp_p} ]; then echo -n "Could not set proper permissions \"a+${ocp_perms}\" " echo "on ${ocp_file}" exit 1 fi } usage() { echo "Usage: ${myname} [-r ] [-p ]" exit 1 } ################### # MAIN ################### CHECK_PROBE=/usr/sbin/install.d/chkprobe # # Parse command line options. All options must be by a separated by a space # while [ "${1}"x != "x" ] ; do case ${1} in -r) # explicit rules file specified if [ ! "${2}" ]; then usage fi RULES=${2} if [ ! -f ${RULES} ]; then echo "No rules file \"${RULES}\" found" exit 1 fi shift 2 ;; -p) if [ ! "${2}" ]; then usage fi CDROM=${2} i_dir=`echo ${CDROM}/Solaris*/Tools/Boot/usr/sbin/install.d` if [ ! -f ${i_dir}/chkprobe ]; then echo "ERROR: ${2} is not a valid Solaris 2.x CD image" exit 1 fi PATH=${i_dir}:${PATH} export PATH CHECK_PROBE=${i_dir}/chkprobe shift 2 ;; -P) # Just check the syntax of a single profile. TEST_PROFILE=${2} shift 2 ;; -n) # Don't make a rules.ok - just check the syntax of the # rules file TESTING=1 shift 1 ;; *) # -anything else is an unknown argument usage ;; esac done # If we're just checking a single profile, do it here - get it out of # the way if [ "X$TEST_PROFILE" != "X" ] ; then profile_file="$TEST_PROFILE" cat "${TEST_PROFILE}" 2> /dev/null | \ deslashify 2> /dev/null | \ check_profile_file || exit $? exit 0 fi # see if we are trying a different rules file if [ ! "${RULES}" ]; then RULES=${STD_RULES} fi # don't define CUSTOM_PROBES, if none exists if [ ! -f "${CUSTOM_PROBES}" ]; then CUSTOM_PROBES="" fi if [ ! -x "${CHECK_PROBE}" ]; then echo "ERROR: Unable to execute /usr/sbin/chkprobe" usage fi egrep -s probe_installed ${CHECK_PROBE} if [ $? -ne 0 ]; then echo "ERROR: The path to the Solaris CD image is required" echo "Use the -p option." usage fi # check rule file using check_rule_file echo "Validating ${RULES}..." check_rule_file ${RULES} if [ $? -ne 0 ]; then exit 1 fi if [ $TESTING = 1 ] ; then exit 0 fi if [ "${RULES}" = "${STD_RULES}" ]; then # Do we have write access to the rules.ok file? touch ${RULES}.ok 2>/dev/null if [ $? -ne 0 ] ; then if [ -f ${RULES}.ok ] ; then echo "ERROR: Could not replace existing ${RULES}.ok" else echo "ERROR: Could not create ${RULES}.ok" fi exit 1 fi # Create the rules.ok file create_derived_file ${RULES} ${RULES}.ok append_checksum ${RULES}.ok ok_check_perms r ${RULES}.ok if [ "${CUSTOM_PROBES}" ]; then # Do we have write access to the custom_probes.ok file? touch ${CUSTOM_PROBES}.ok 2>/dev/null if [ $? -ne 0 ] ; then if [ -f ${CUSTOM_PROBES}.ok ] ; then echo "ERROR: Could not replace existing" \ ${CUSTOM_PROBES}.ok else echo "ERROR: Could not create" \ ${CUSTOM_PROBES}.ok fi exit 1 fi create_derived_file ${CUSTOM_PROBES} ${CUSTOM_PROBES}.ok append_checksum ${CUSTOM_PROBES}.ok ok_check_perms rx ${CUSTOM_PROBES}.ok fi else # only create a .ok file when checking the standard rules file echo "${RULES}.ok file not created" fi rm -f /tmp/*.$$ # get rid of tmp files.... echo "The custom JumpStart configuration is ok." exit 0