#!/bin/ksh93 LIC='[-?$Id$ ] [-copyright?Copyright (c) 2014 Jens Elkner. All rights reserved.] [-license?CDDL 1.0]' SDIR=${.sh.file%/*} typeset -r FPROG=${.sh.file} typeset -r PROG=${FPROG##*/} for H in log.kshlib man.kshlib ; do X=$SDIR/$H [[ -r $X ]] && . $X && continue X=${ whence $H; } [[ -z $X ]] && print "$H not found - exiting." && exit 1 . $X done unset H typeset -T HBA_t=( typeset -h 'Short HBA name (e.g. 9200-8e) - a cosmetical value' name='' typeset -h 'The download package name of the HBA (e.g. 9200-8e). Will be used as default for the 1st try when downloading stuff' pname='' typeset -h 'The HBA type (e.g. SAS_SATA_6G or SAS_SATA_12G)' type='' typeset -i -h 'Skip downloading any version lower than this (integer, i.e. w/o the P prefix) - minor optimization' notbelow=10 function getURL { typeset X="$1" X="${X//@VER@/${VERSION}}" X="${X//@TYPE@/${_.type}}" X="${X//@PKG@/${_.pname}}" print -n "$X" } typeset -fh 'Internal helper for get*URL functions.' getURL function getFwURL { _.getURL "${FW_URL}" } typeset -fh 'Get the FW URL for this HBA. The related archive contains the BIOS (mptsas*.rom) as well as the firmware (*.bin).' getFwURL function getInstallerURL { _.getURL "${SOLINSTALL_URL}" } typeset -fh 'Get the Solaris Installer URL for this HBA. It contains the sas2flash utility, which can be used to flash the HBA from a running Solaris environment. Usually it is the same for all HBAs of the same type (i.e. 6G or 12G)' getInstallerURL function getSas2ircuURL { _.getURL "${SAS2IRCU_URL}" } typeset -fh 'Get the SAS2IRCU URL for this HBA. It contains the sas2ircu utility, which can be used to obtain some information about the installed HBAs even in IT mode. Usually it is the same for all HBAs of the same type (i.e. 6G or 12G)' getSas2ircuURL ) FW_URL='http://www.lsi.com/downloads/Public/Host%20Bus%20Adapters/Host%20Bus%20Adapters%20Common%20Files/@TYPE@_@VER@/@PKG@_Package_@VER@_IT_Firmware_BIOS_for_MSDOS_Windows.zip' SOLINSTALL_URL='http://www.lsi.com/downloads/Public/Host%20Bus%20Adapters/Host%20Bus%20Adapters%20Common%20Files/@TYPE@_@VER@/Installer_@VER@_for_Solaris.zip' SAS2IRCU_URL='http://www.lsi.com/downloads/Public/Host%20Bus%20Adapters/Host%20Bus%20Adapters%20Common%20Files/@TYPE@_@VER@/SAS2IRCU_@VER@.zip' REFERRER='/Pages/user/eula.aspx?Source=http://www.lsi.com/sep/Pages/oracle/sg_x_sas6-r-int-z.aspx' # Actually this should be at the top of the script, but can't, because we need # the type definition first. Some [lazy] people may prefer to put this part # into a separate file and source it in here. We save all stuff with the KEY # name. HBA_t -A HBA HBA['9200_8e']=( name='9200-8e' pname='9200-8e' type='SAS_SATA_6G' ) HBA['9211_8i']=( name='9211-8i' pname='9211-8i' type='SAS_SATA_6G' ) HBA['9207_8e']=( name='9207-8e' pname='9207_8e' type='SAS_SATA_6G' notbelow=14 ) HBA['9207_8i']=( name='9207-8i' pname='9207_8i' type='SAS_SATA_6G' notbelow=14 ) function showUsage { typeset WHAT="$2" getopts -a "$PROG" "${ print ${Man.FUNC[$WHAT]}; }" OPT --man } Man.addFunc getHbaNames '' '[+NAME?getHbaNames - get supported HBA names or keys] [+DESCRIPTION?Get the names of all HBAs supported by this script. If a non-empty \aarg\a is given, the human readable names are printed to stdout, otherwise the internally used keys for all \bHBA\b records.] \n\n[\aarg\a] ' function getHbaNames { typeset NAMES='' X for X in "${!HBA[@]}" ; do [[ -z $1 ]] && NAMES+="$X " || NAMES+="${HBA[${X}].name} " done print "${NAMES}" } function forAll { typeset -A TYPES typeset CMD="$1" URL TYPE IN PRE NAME SUF CNAME integer FORCE=0 [[ -n $2 ]] && FORCE=$2 if [[ ${CMD} == 'download' ]]; then if [[ ! -d ${BASEDIR} ]] && ! mkdir -p ${BASEDIR} ; then Log.fatal "Unable to create directory '${BASEDIR}' - exiting." return 1 fi if [[ ! -d ${BASEDIR}/archives ]] && ! mkdir ${BASEDIR}/archives ; then Log.fatal "Unable to create directory 'archives' - exiting." return 2 fi if ! cd ${BASEDIR}/archives ; then Log.fatal "Unable to cd to 'archives' - exiting" return 3 fi fi typeset V=${VERSION:1} V=${V%.*} for X in "${!HBA[@]}" ; do (( $V < ${HBA[${X}].notbelow} )) && continue URL="${HBA[${X}].getFwURL}" TYPE="${HBA[${X}].type}" TYPES[${TYPE}]="$X" [[ ${CMD} == 'list' ]] && print "${X}:\n\t${URL}\n\n" && continue [[ ${CMD} != 'download' ]] && continue TYPE="${X//-/_}-FW.zip" (( ! FORCE )) && [[ -f "${TYPE}" ]] && continue integer INTERNAL=0 [[ ${TYPE: -8:1} == 'i' ]] && INTERNAL=1 Log.info "Downloading $X FW ..." # O curl '-#' -e "${REFERRER}" -L -o "${TYPE}" "${URL}" [[ ! -f ${TYPE} ]] && IN='' || read -n 2 IN < "${TYPE}" [[ ${IN} == 'PK' ]] && continue PRE=${URL%/*} NAME=${.sh.match%_Package*} SUF=${.sh.match} CNAME=${NAME//-/_} [[ -z ${.sh.match} ]] && CNAME=${NAME//_/-} Log.info "Downloading $X FW (2nd try) ..." # AR (( INTERNAL )) && SUF=${SUF//_IT/_IR_IT} curl '-#' -e "${REFERRER}" -L -o "${TYPE}" "${PRE}${CNAME}${SUF}" [[ ! -f ${TYPE} ]] && IN='' || read -n 2 IN < "${TYPE}" [[ ${IN} == 'PK' ]] && continue Log.info "Downloading $X FW (3rd try) ..." # OR (( ! INTERNAL )) && SUF=${SUF//_IT/_IR_IT} curl '-#' -e "${REFERRER}" -L -o "${TYPE}" "${PRE}${NAME}${SUF}" [[ ! -f ${TYPE} ]] && IN='' || read -n 2 IN < "${TYPE}" [[ ${IN} == 'PK' ]] && continue Log.info "Downloading $X FW (last try) ..." # A (( INTERNAL )) && SUF=${SUF//_IR_IT/_IT} curl '-#' -e "${REFERRER}" -L -o "${TYPE}" "${PRE}${CNAME}${SUF}" [[ ! -f ${TYPE} ]] && IN='' || read -n 2 IN < "${TYPE}" if [[ ${IN} != 'PK' ]]; then Log.warn "${TYPE} failed." rm -f "${TYPE}" fi done for X in "${!TYPES[@]}" ; do TYPE=${TYPES[${X}]} URL="${HBA[${TYPE}].getInstallerURL}" URL2="${HBA[${TYPE}].getSas2ircuURL}" [[ ${CMD} == 'list' ]] && print "${X}:\n\t${URL}\n\t${URL2}\n\n" && continue [[ ${CMD} != 'download' ]] && continue TYPE="Solaris-${X//-/_}-Installer.zip" (( ! FORCE )) && [[ -f "${TYPE}" ]] && continue Log.info "Downloading $X Installer ..." curl '-#' -e "${REFERRER}" -L -o "${TYPE}" "${URL}" [[ ! -f ${TYPE} ]] && IN='' || read -n 2 IN < "${TYPE}" if [[ ${IN} != 'PK' ]]; then Log.warn "${TYPE} failed." rm "${TYPE}" fi TYPE="SAS2IRCU-${X//-/_}.zip" (( ! FORCE )) && [[ -f "${TYPE}" ]] && continue Log.info "Downloading $X SAS2IRCU ..." curl '-#' -e "${REFERRER}" -L -o "${TYPE}" "${URL2}" [[ ! -f ${TYPE} ]] && IN='' || read -n 2 IN < "${TYPE}" if [[ ${IN} != 'PK' ]]; then Log.warn "${TYPE} failed." rm -f "${TYPE}" fi done Log.info "Downloads done. See ${BASEDIR}/archives/ ." } function copy { typeset SRC="$1" DST="$2" ALT="$3" MODE="$4" [[ ! -f ${SRC} ]] && return if [[ -e ${DST} ]]; then cmp "${SRC}" "${DST}" && return [[ -n ${ALT} ]] && ALT="-${ALT}" DST="${DST%.*}${ALT}${sh.match}" fi mv "${SRC}" "${DST}" chmod ${MODE} "${DST}" } function extract { if ! cd ${BASEDIR} ; then Log.fatal "Unable to change directory to ${BASEDIR}'" return 1 fi if [[ ! -d docs ]] && ! mkdir docs ; then Log.fatal "Unable to create 'docs' directory - exiting" return 2 fi if [[ -e tmp ]] && ! rm -rf tmp ; then Log.fatal "Unable to remove 'tmp' directory - exiting" return 3 fi typeset F X HBAN TYPE for X in ~(N)archives/*.zip ; do F=${X:9} if [[ ${F: -7} == '-FW.zip' ]]; then HBAN=${F:0:${#F}-7} elif [[ ${F: -14} == '-Installer.zip' && ${F:0:8} == 'Solaris-' ]]; then TYPE=${F:8:${#F}-22} HBAN='' elif [[ ${F:0:9} == 'SAS2IRCU-' ]]; then TYPE=${F:9} TYPE=${TYPE%.zip} HBAN='' else continue fi Log.info "Extracting $X ..." if ! unzip -d tmp -qq "$X" ; then Log.warn "failed and skipped." continue fi mv tmp/* tmp/x if [[ -z ${HBAN} ]]; then if [[ ${F:0:9} == 'SAS2IRCU-' ]]; then copy "${ ls tmp/x/*_dos_*/sas2ircu.exe ; }" sas2ircu.exe \ ${TYPE} 0644 copy "${ ls tmp/x/*_solaris_x86_*/sas2ircu ; }" sas2ircu \ ${TYPE} 0755 else # Installer - we take x86, only. copy "${ ls tmp/x/*x86*/sas2flash ; }" sas2flash ${TYPE} 0755 fi HBAN=${TYPE} else # HBA copy "${ ls tmp/x/*/*IT/*.bin ; }" "${HBAN}.bin" '' 0644 F="${ ls tmp/x/sasbios_rel/*.rom ; }" X="${F##*/}" copy "${F}" "${X}" ${HBAN} 0644 F="${ ls tmp/x/sasbios_rel/*.lbf 2>/dev/null ; }" [[ -n $F ]] && copy "${F}" "${F##*/}" ${HBAN} 0644 F="${ ls tmp/x/sasbios_rel/*.txt ; }" copy "${F}" "docs/${X%.rom}.txt" ${HBAN} 0644 F="${ ls tmp/x/sas2flash_dos_rel/sas2flsh.exe ; }" copy "${F}" "${F##*/}" ${HBAN} 0644 fi for F in tmp/x/*.{txt,pdf} ; do copy "${F}" "docs/${F##*/}" ${HBAN} 0644 done rm -rf tmp done } function clean { [[ ! -d ${BASEDIR} ]] && return cd "${BASEDIR}" || return typeset X typeset -a FILES for X in ~(N)* ; do [[ $X == 'archives' ]] && continue FILES+=( "${X}" ) done [[ -n ${FILES} ]] && rm -rf "${FILES[@]}" } Man.addFunc MAIN '' '[+NAME?'"$PROG"' - download SAS HBA firmware] [+DESCRIPTION?Downloads the firmware and some utilities for the following SAS HBAs in a row:]{ [+?'"${ getHbaNames 1; }"'] } [+?Optionally it allows one to extract the relevant parts of the downloaded archives and re-arrange them in a less redundant way/flatten directory tree.] [h:help?Print this help and exit.] [F:functions?Print out a list of all defined functions. Just invokes the \btypeset +f\b builtin.] [H:usage]:[function?Show the usage information for the given function if available and exit. See also option \b-F\b.] [T:trace]:[fname_list?A comma or whitspace separated list of function names, which should be traced during execution.] [b:basedir]:[path?Directory where to create the package subdirectory. The package subdirectory will be used to store downloaded files and optionally extract its content. If the sub directory does not yet exist, it gets created. Default: the current working directory.] [c:clean?Clean up the package directory from anything except archives/* before doing anything else.] [d:download?Download all missing FW and utilities.] [D:force?Force downloading all FW and utilities even if it appears, that they already have been downloaded.] [l:list?Just list the FW and Installer download URLs for all known HBAs and exit.] [x:extract?Extract all related FW and Installer .zip files in the corresponding directory [after downloading them]] and re-arrange them in a more suitable structure.] [v:version]:[pname?The package name to download. E.g. P18 .] [+EXAMPLES]{ [+?'"${PROG}"' -v18 -b /tmp -cdx] } [+NOTES?This script targets IT firmware, BIOS and the sas2flash + sas2ircu utilities for Solaris and DOS for LSI SAS-HBAs, only. However, it is extensible to support RAID firmware and other stuff as well ;-).] ' X="${ print ${Man.FUNC[MAIN]} ; }" integer CMD=0 FORCE=0 VERSION='' BASEDIR="${PWD}" while getopts "${X}" option ; do case "$option" in h) showUsage ${PROG} MAIN ; exit 0 ;; F) typeset +f ; exit 0 ;; H) if [[ ${OPTARG%_t} != $OPTARG ]]; then $OPTARG --man # self-defined types else showUsage "$OPTARG" "$OPTARG" # function fi exit 0 ;; T) typeset -ft ${OPTARG//,/ } ;; l) (( CMD|=1 )) ;; v) VERSION="${OPTARG}" if [[ ${VERSION:1} == 'P' ]]; then VERSION=${VERSION:1} fi if [[ ${VERSION} =~ [0-9]+(\.[0-9]+)? ]]; then VERSION="P${VERSION}" else Log.fatal "Invalid version '${VERSION}' - use something like 'P15'" exit 1 fi ;; b) BASEDIR="${OPTARG}" ;; d) (( CMD|=2 )) ;; D) (( CMD|=2 )) ; FORCE=1 ;; x) (( CMD|=4 )) ;; c) (( CMD|=8 )) ;; esac done X=$((OPTIND-1)) shift $X (( ! CMD )) && { showUsage ${PROG} MAIN ; exit 0; } if [[ -z ${VERSION} ]]; then Log.fatal 'No version specified! See "'"${PROG}"' -h".' exit 1 fi BASEDIR="${BASEDIR}/${VERSION}" (( CMD & 1 )) && { forAll list ; exit $? ; } (( CMD & 8 )) && clean (( CMD & 2 )) && { forAll download ${FORCE} || exit $? ; } (( CMD & 4 )) && { extract || exit $? ; }