#!/bin/sh

# NOTE: our output is going to /mnt/ramdisk/codeload.progress

source /usr/bin/utils.sh
source /usr/bin/fcns.shinc

actComp="/"

SUCCESS=255

# Tried to move call to killnonessential out of ftpd.c and call it
# later in this script.  That would allow us to avoid a reboot until
# the bundle was validated to be somewhat reasonable.   Unfortunately,
# that was a no-go.  It had some side-effects that I ran out of
# time tracking down.  So, if this script is called, you're gonna reboot.
DO_REBOOT=false
DO_REBOOT_SC=false

# The progressmonitor is workaround for ftp client timeouts.
# It outputs a message every so often if nothing has been added
# to the codeload.progress file (which means also output to client).
# It is killed in cleanup().
progressmonitor /mnt/ramdisk/codeload.progress &
PMPID=$!

# =====================================================================
# CONSTANTS
REJ_CPLD_REG_PREFACE=WORKSON_cpld_
REJ_ERR_PREFACE="ERROR: Reject Bundle:"
# =====================================================================

# =====================================================================
# No matter what, run this function on exit
# =====================================================================
exitTrapFcn()
{
    local SUCCESS=$?

    # Don't touch anything if we are the second docodeload script
    if ! $singleton; then
        exit 0
    fi
    rm -f /mnt/ramdisk/singleton.codeload

    if [ "$SUCCESS" -ne "0" ]
    then
        # CAUTION: Some script writers (specifically, at Sepaton) are keying off
        # the phrase "Code Load Fail.", so don't change this wording.  Also, don't
        # change the phrase "Code load completed successfully." in this file for the same reason.
        # Note: Plain echo is for FTP output; redirected echo is for WBI output.
        echo "*** Code Load Fail. ***"
        echo "STATUS: *** Code Load Fail. ***"
        if $DO_REBOOT; then
            echo "Restarting Management Controller..."
            echo "RETURN_CODE: 10"
        else
            echo "RETURN_CODE: 11"
        fi
        apiError $actComp 1000 "Code load failed."
    else
        echo "Code load completed successfully."
        echo "STATUS: Code load completed successfully."
        if $DO_REBOOT; then # some kind of reboot will take place
            if $DO_REBOOT_SC; then # both the SC and MC
                finishScCodeload
                echo "Restarting Storage Controller and Management Controller. This will take a couple of minutes."
            else # only MC
                echo "Restarting Management Controller..."
            fi
            echo "RETURN_CODE: 8"
        else # no reboot
            echo "RETURN_CODE: 9"
        fi
        apiOK $actComp
    fi

    if [ -f  /mnt/ramdisk/codeload.progress ]
    then
       cp /mnt/ramdisk/codeload.progress /cfg/last.codeload.progress
    fi

    if $DO_REBOOT; then
        # Delay so messaging can get out to WBI.
        disconnectFTP
        if $DO_REBOOT_SC; then
            # we're going to hit the board, so we need to ensure the SC is shutdown
            /usr/bin/capi.app --prepare -x
            safeRebootSc
        else # MC only
            /usr/bin/reboot.sh 30
        fi
        cleanup
    else # No reboot - pause while cleaning up
        cleanup pause
    fi

    return $SUCCESS
}
trap exitTrapFcn SIGINT SIGTERM EXIT

# =====================================================================
# $1 -- Name of the cpld register to read
# $2 -- returns:  true = it matches; false = mismatch
# =====================================================================
chkCpldReg()
{
    local cpldReg="$1"

    # Use the name of the cpld register to look up the bundleinfo value
    # and to read the register on the controller

    local worksOnName
    eval worksOnName=$REJ_CPLD_REG_PREFACE$cpldReg

    local worksOnVal
    eval worksOnVal=\$$worksOnName

    local reg
    reg=$(cat /proc/$MC_HWARCH/cpld/$cpldReg)

    # Multiple valid choices
    local item
    local foundit=false
    for item in $worksOnVal; do
        if [ "$item" == "$reg" ]; then
            foundit=true
            break
        fi
    done

    if ! $foundit; then
        echo "$REJ_ERR_PREFACE $cpldReg mismatch"
    fi

    eval $2=$foundit
}

# =====================================================================
# $1 -- returns:  true = all match; false = at least one mismatch
# =====================================================================
chkAllCpldRegs()
{
    local allVars=$(set | grep "^$REJ_CPLD_REG_PREFACE" \
                        | sed -e "s~$REJ_CPLD_REG_PREFACE~~g" -e "s~=.*~~g")

    local rc=true
    local item
    for item in $allVars; do
        chkCpldReg "$item" rc

        ! $rc && break
    done

    eval $1=$rc
}

# =====================================================================
# $1 -- returns:  true = all match; false = at least one mismatch
# =====================================================================
chkXmlSettings()
{
    local xml
    local item
    local portType
    local foundit=true

    if [ -n "$WORKSON_xml_portType" ]; then
        # Assumes entries look like this:
        # <MEMBER name="portType" type="symbol">FC</MEMBER>

        xml=$(grep 'MEMBER name="portType' /cfg/sysinfo/system.xml\
           | sed -e 's/MEMBER.*"//g' -e 's/\/MEMBER//' -e 's/<//g' -e 's/>//g')

        # *all* the portType's found in the xml must match one of the
        # valid choices declared in bundleinfo
        foundit=false;
        for portType in $xml; do
            [ "$portType" == "Unknown" ] && continue

            foundit=false;
            for item in $WORKSON_xml_portType; do
                if [ "$item" == "$portType" ]; then
                    foundit=true
                    break
                fi
            done

            if ! $foundit; then
                echo "$REJ_ERR_PREFACE portType '$portType' not supported"
                break;     # failed
            fi
        done
    fi

    eval $1=$foundit
}

# =====================================================================
# Check if the image being loaded will operate on a EX/BX SAS
# Code GL200 and older not supported
#
# $1 -- returns:  true = all match; false = at least one mismatch
# =====================================================================
chkSasCompatible()
{
    local bundleVer="$1"
    local passed=true

    local relMajor="${bundleVer:3:3}"
    local portType=$(cat /proc/$MC_HWARCH/cpld/board_config)

    grep 'MEMBER name="portType' /cfg/sysinfo/system.xml\
                   | sed -e 's/MEMBER.*"//g' -e 's/\/MEMBER//' -e 's/<//g' -e 's/>//g'|grep -iq sas
    # If there is a SAS host and this is GL200 or older on a EX or BX controller, Fail
    if [ $? -eq 0 ]; then
        if [ "GL" == "${bundleVer:0:2}" ]; then
            if [ $relMajor -le 200 ]; then
                case $portType in
                    3)
                        echo "$REJ_ERR_PREFACE Board type 'BX' not supported for bundle $bundleVer"
                        passed=false
                        ;;
                    1)
                        echo "$REJ_ERR_PREFACE Board type 'EX' not supported for bundle $bundleVer"
                        passed=false
                        ;;
                    *)
                        echo "PortType '$portType' supported for bundle $bundleVer"
                        ;;
                esac
            fi
        fi
    fi
    eval $2=$passed
}

# =====================================================================
# Will set global WORKSON_... variables if it completes w/o error
# $1 -- bundle version string
# $2 -- returns:  true = can generate; false = bundle is incompatible
# =====================================================================
genDefaultBuildInfo()
{
    local bundleVer="$1"

    eval $2=false;  # assume it won't work

    if [ "GL" != "${bundleVer:0:2}" ]; then
        echo "$REJ_ERR_PREFACE Bundle is for a different controller type '${bundleVer:0:2}'"
        return
    fi
    local relMajor="${bundleVer:3:3}"

    WORKSON_cpld_board_type=348;      # 348-Gallium
    if [ "$relMajor" == "100" ]; then
        WORKSON_cpld_board_config=0
        WORKSON_xml_portType="FC"
    elif [ "$relMajor" == "101" ]; then
        WORKSON_cpld_board_config=0
        WORKSON_xml_portType="FC SAS iSCSI"
    else
        WORKSON_cpld_board_config="0 1";  # 0-LX, 1-EX, 2-FX
        WORKSON_xml_portType="FC SAS iSCSI"
    fi

    eval $2=true
}

# =====================================================================
# $1 -- returns:  true = bundle is compatible
# =====================================================================
compatibleBundle()
{
    eval $1=false;   # assume that it will fail
    local bundleVer=$(inicntrl /mnt/ramdisk/system/version.txt MC_system version)

    # reject any bundle known to brick us.  Add 'em here
    if [ "$bundleVer" == "GLxxx_fictious_bricker" ]; then
        echo "$REJ_ERR_PREFACE Bundle '$bundleVer' has known issues and will not be loaded."
        return
    fi

    if [ -e bundleinfo ]; then
        source bundleinfo
    else
        # older bundles w/o this rejection logic
        local gotVals
        genDefaultBuildInfo "$bundleVer" gotVals
        ! $gotVals && return
    fi

    local allCpldRegs
    chkAllCpldRegs allCpldRegs
    ! $allCpldRegs && return

    local xmlOK
    chkXmlSettings xmlOK
    ! $xmlOK && return

    local sasOK
    chkSasCompatible "$bundleVer" sasOK
    ! $sasOK && return

    eval $1=true;   # success - got thru all the checks
}

cleanupViaMd5()
{
    local filename
    local folder
    local md5file=allfiles.md5

    # delete all files and folders referenced in the md5sum file

    oldIFS=$IFS
    IFS=$'\n'
    for line in $(cat $md5file); do
        filename=${line#*\./}
        rm -f $filename

        # folders have at least one slash
        case "$filename" in
          */*)
            folder=${filename%%/*}
            rm -rf $folder/
            ;;
        esac

    done
    IFS=$oldIFS

    rm -f $md5file
}

cleanup ()
{
    # need to pause for a few seconds to get last echo text
    [ "$1" == "pause" ] && sleep 4

    logger "Cleaning up from docodeload"

    [ -f allfiles.md5 ] && cleanupViaMd5

    # Clean up any files that are left lying around.
    rm bundle.bin 2>/dev/null
    rm -f bundleinfo 2>/dev/null
    rm -f capi.app 2>/dev/null
    rm -f clixml.sh 2>/dev/null
    rm -f comparelevel.awk 2>/dev/null
    rm -f contents.xml 2>/dev/null
    rm update.sh 2>/dev/null
    rm -f utils.sh 2>/dev/null
    rm image.fla 2>/dev/null
    rm -f do.* 2>/dev/null
    rm -f showsystem.cmd 2>/dev/null
    rm -f pokesc.bin 2>/dev/null
    rm -f returnlevel.awk 2>/dev/null
    rm -f is.* 2>/dev/null
    rm fifo 2>/dev/null
    rm encl* 2>/dev/null
    rm disk* 2>/dev/null
    rm listOf* 2>/dev/null
    rm -rf bundle 2>/dev/null
    rm -rf cpld 2>/dev/null
    rm -rf mc 2>/dev/null
    rm -rf sc 2>/dev/null
    rm -rf ec 2>/dev/null
    rm -rf system 2>/dev/null
    rm -rf bin 2>/dev/null
    kill -9 $PMPID
}

singleton=true
# Check to see if docodeload is already running
set -o noclobber
(echo "" > /mnt/ramdisk/singleton.codeload) 2> /dev/null
if [ $? -ne 0 ]; then
    apiError $actComp 2000 "docodeload is already active."
    singleton=false
    exit 1
fi
set +o noclobber

echo -n "Starting $0: "
date
cd /mnt/ramdisk

if [ -f is.bundle ]; then
    compatibleBundle isCompat
    if ! $isCompat; then
        echo "*** Code Load Rejected. Does not appear to be a compatible bundle. ***"
        apiError $actComp 1004 "Code load failed: incompatible bundle type."
        cleanup pause
        exit 11
    fi
fi

# TODO: may want to eliminate this check after we finish mtd layout transition
if [ ! -f is.spiboot ] && [ -f is.bundle ];then
    echo "*** Code Load Rejected. Older Codeload bundle is not compatible. ***"
    echo "*** This bundle was built before the introduction of the SPI Flash. ***"
    apiError $actComp 1002 "Code load failed."
    cleanup pause
    exit 11
fi

# here we roll progress files
rollProgress

# check to see if getlogs or managedlogs is occurring before we start - getlogs.progress is the first thing those operations touch
if [ -f getlogs.progress ]
then
    echo "*** Code Load Fail. Managed logs or get logs operation in progress. Aborting codeload operation. ***"
    echo "STATUS: *** Code Load Fail. Managed logs or get logs operation in progress. Aborting codeload operation. ***"
    echo "RETURN_CODE: 11"
    apiError $actComp 1003 "Codeload and getlogs attempted simultaneously."
    cp /mnt/ramdisk/codeload.progress /cfg/last.codeload.progress
    cleanup pause
    exit 11
fi

# the way to identify if it's pfu or a bundle is that there's an included file called is.bundle for a codeload bundle
# is.pfu for a pfu bundle
apiActive $actComp "Checking bundle."
if [ -f is.pfu ] && [ -f is.bundle ]
then
    # both pfu and a codeload have been attempted at the same time (uploading)
    echo "*** Code Load Fail. PFU failed. Codeload and PFU attempted simultaneously, aborting operation. ***"
    echo "*** Code Load Fail. PFU failed. Codeload and PFU attempted simultaneously, aborting operation. ***" >> /cfg/pfu/pfu.log
    echo "STATUS: *** Code Load Fail. PFU failed. Codeload and PFU attempted simultaneously, aborting operation. ***"
    echo "RETURN_CODE: 11"
    apiError $actComp 1001 "Codeload and PFU attempted simultaneously."
    cp /mnt/ramdisk/codeload.progress /cfg/last.codeload.progress
    cleanup pause
    exit 11
fi

# if dopfunu or dopfu called this, turn off some checking
chkDopfunu=true
[ "$1" == "calledByPFU" ] && chkDopfunu=false

if [ "$1" == "force" ]
then
        touch do.force
fi

chkOtherLoading=true
if [ -f image.fla ]; then
    chkOtherLoading=false
    echo "WARNING: Loading SC image, skipping simultaneous codeload check"
fi

if $chkOtherLoading; then
    is-othermc-codeloading.sh $chkDopfunu >/dev/null 2>&1
    if [ $? -eq 0 ]; then
        if [ -f /cfg/.eng/codeload.allow.simultaneous -o -f do.force ]; then
            echo "WARNING: Simultaneous codeloads detected but continuing due to override"
        else
            # both pfu and a codeload have been attempted at the same time (uploading)
            echo "*** Code Load or Partner Firmware Update is in progress on the other MC, aborting operation. ***"
            echo "*** Code Load or Partner Firmware Update is in progress on the other MC, aborting operation. ***" >> /cfg/pfu/pfu.log
            echo "STATUS: *** Code Load or Partner Firmware Update is in progress on the other MC, aborting operation. ***"
            echo "RETURN_CODE: 11"
            apiError $actComp 1001 "Code Load or Partner Firmware Update is in progress on the other MC."
            cp /mnt/ramdisk/codeload.progress /cfg/last.codeload.progress
            cleanup pause
            exit 11
        fi
    fi
fi

# Passed all the 1st level checks...

# Check if we received a full bundle (MC + SC + EC etc. .bin file) or partial bundle (MC-only .bin file).
if [ -f update.sh ]
then
    local proceed=0
    if [ -x /cfg/codeload/bundlehook.sh ]
    then
        /cfg/codeload/bundlehook.sh
        proceed=$?
    elif [ -f /usr/bin/bundlehook.sh ]
    then
        if [ ! -x /usr/bin/bundlehook.sh ]
        then
            chmod +x /usr/bin/bundlehook.sh
        fi
        /usr/bin/bundlehook.sh
        proceed=$?
    fi
    if [ "$proceed" -eq "0" ]
    then
        echo "Loading bundle"
        apiActive $actComp "Loading bundle."
        ./update.sh
        rc=$?
        if [ -f do.reboot ]; then
            DO_REBOOT=true
        else
            DO_REBOOT=false
        fi
        exit $rc
    else
        echo "STATUS: *** Code Load Fail. Pre-condition for bundle load not met. ***"
        cp /mnt/ramdisk/codeload.progress /cfg/last.codeload.progress
        cleanup pause
        exit 255
    fi
fi

# Check if we received an SC-only image or SC bundle (including SC loader, memory controller, etc.).
# These files are in FLA format
if [ -f image.fla ]
then
    ###DO_REBOOT_SC=true
    apiActive $actComp "Loading SC image."
    /usr/bin/dosccodeload $1
    exit $?
fi

# Check for enclosure and disk firmware file update
if [ -f encl* ]
then
    DO_REBOOT=false
    apiActive $actComp "Loading enclosure image."
    /usr/bin/doeccodeload $1
    exit $?
fi

if [ -f disk* ]
then
    DO_REBOOT=false
    apiActive $actComp "Loading disk image."
    /usr/bin/dodiskcodeload $1
    exit $?
fi

# If a valid bundle, would exit before getting to this point
echo "*** Code Load Rejected. Does not appear to be a bundle at all. ***"
apiError $actComp 1004 "Code load failed: not a bundle type."
cleanup pause
exit 255
