#!/bin/sh
# options: pushcfg, pushmc

export BD="/mnt/ramdisk/bundle"

source /usr/bin/utils.sh

actComp="/"

# note that progress and log messages can be different, and are
# definitely different locations for output for UI versus persistance purposes

PFULOG="/cfg/pfu/pfu.log"
PFULOG2="/cfg/pfu/pfu2.log"
PFUPROGRESS="/mnt/ramdisk/pfu.progress"

# Create if /cfg/pfu dne
[ ! -d "/cfg/pfu" ] && mkdir -p /cfg/pfu

# quick message function to write to PFU log
msg ()
{
    echo "`date`: $*" >> $PFULOG

    # poor-mans method to limit the length of the PFU progress file
    _flength=$(wc -l $PFULOG | awk '{ print $1; }')
    if [ $_flength -gt 512 ]
    then
        mv $PFULOG $PFULOG2
        rm $PFULOG
        tail -n 256 $PFULOG2 >$PFULOG
        rm $PFULOG2
    fi
}


# make sure we are not loading code to ourselves
source "fcns.shinc";  # lib of shell fcns

# check for any activity on either node, exclude "dopfunu and pfu.progress", since that's us
# this is a better check than before because it's checking both nodes for other conflicting activities
# however, it has the issue that it skips checking for the other guy trying to PFU to us.
# this is just temporary until I can move it into the libplatform
cla --query-activity "Either:any" --exclude "dopfunu pfu.progress"
RESULT=$?
if [ "$RESULT" == "1" ]
then
    apiError $actComp 2000 "Other Activity (codeload,pfu,getlogs) in progress."
	exit 0
fi

genIntegrityFile()
{
    (
        cd ${BD}
        find . -type f | sort -f \
            | grep -v "\.md5" | xargs md5sum -b > allfiles.md5
    )
}

checkpushsuccess()
{
    rpcsys othermcinner "ls /mnt/ramdisk/is.pfu >/mem/isPFUpresent"
    RESULT=$?
    if [ "$RESULT" == "0" ]
    then
        rm /mem/other_isPFUpresent
        rpcget othermcinner /mem/isPFUpresent /mem/other_isPFUpresent
        _flength=$(wc -l /mem/other_isPFUpresent | awk '{ print $1; }')
        if [ $_flength -gt 0 ]
        then
            msg "Pushing PFU bundle to other MC successful"
            pushState='0'
        else
            pushState='1'
        fi
    else
        msg "PFU failed to push to the other MC"
        pushState='1'
    fi
}

pushbundle()
{
    # options are: [ cfg ] | [ fw ] | [ ver ]
    # cfg - send config over
    # fw - send firmware over
    # ver - send version.txt over

    cfg=false
    fw=false
    ver=false

    for var in "$@"
    do
        case "$var" in
        "cfg")
        cfg=true
        ;;
        "fw")
        fw=true
        ;;
        "ver")
        ver=true
        ;;
        *)
        echo ""
        ;;
        esac
    done

    echo "cfg=$cfg, fw=$fw, ver=$ver"

    if $ver; then
        apiActive $actComp "Copying version.txt to other MC."
        rpcput othermcinner /cfg/version.txt /cfg/version.txt
        sleep 2
    fi

    if [ $cfg -o $fw ]; then
        buildbundle $@
    fi

    if [ -d ${BD} ]
    then
        # a bundle was created

        # now we need to create the allfiles.md5
        genIntegrityFile
        msg "PFU bundle built"
        apiActive $actComp "Copying bundle file to other MC."
        msg "Bundle file starting sending to other MC"
        cd ${BD}
        tar c * | pfuc othermcinner
        msg "Bundle file sent to other controller"
        sleep 5
        checkpushsuccess
        if [[ "$pushState" == "1" ]]
        then
            apiError $actComp 2010 "PFU was unable to push firmware to the partner MC. Aborting."
            msg "PFU failed - unable to push fw"
            rm -rf ${BD}
            exit 1
        else
            apiActive $actComp "Starting codeload on other MC."
            rpcsys othermcinner "{ /mnt/ramdisk/docodeload calledByPFU >> /mnt/ramdisk/codeload.progress; } 2>&1 | while read; do echo \"[dbg] \$REPLY\"; done >> /mnt/ramdisk/codeload.progress"
            msg "PFU successful"
            rm -rf ${BD}
        fi
    else
        msg "ERROR: buildbundle was called but no bundle file was created"
        apiError $actComp 2006 "Failed creating bundle file."
        exit 1
    fi

    if $ver; then
        apiActive $actComp "Checking validity of pushed version.txt"
        rpcget othermcinner /cfg/version.txt /mem/other_version.txt
        diff /cfg/version.txt /mem/other_version.txt > /dev/null
        if [ $? -ne 0 ]; then
            apiActive $actComp "version.txt files differ. Resending.."
            rpcput othermcinner /cfg/version.txt /cfg/version.txt
            sleep 5
        else
            apiActive $actComp "Other's version.txt is valid."
        fi
        rm -f /mem/other_version.txt
        apiActive $actComp "Rebooting other MC."
        /usr/bin/rebootothermc.sh
    fi
}

push2other()
{
    # options to function: $1 pushmc | false; $2 pushcfg | false
    # only push over a full bundle if there's code to be loaded

    bundleopts=""
    if [[ "$1" == "pushmc" ]]
    then
        bundleopts="fw"
    fi
    if [[ "$2" == "pushcfg" ]]
    then
        bundleopts="$bundleopts cfg"
    fi

    if [[ "$1" == "pushmc" ]]
    then
        if [ \( "$LOCAL_VERSION" != "$REMOTE_VERSION" -o "$LOCAL_CPLDversion" != "$REMOTE_CPLDversion" \) ]
        then
            msg "Local Version: $LOCAL_VERSION : $LOCAL_CPLDversion and Partner Version: $REMOTE_VERSION : $REMOTE_CPLDversion"

            pushbundle $bundleopts

        else # no code to be loaded, but the bundle version is different
            msg "Local (active) bundle version: $LOCAL_VERSION_BUNDLE, Remote bundle version: $REMOTE_VERSION_BUNDLE"
            msg "Starting transfer of version.txt file to other MC"

            bundleopts="$bundleopts ver"
            pushbundle $bundleopts

        fi
    else
        if [[ "$2" == "pushcfg" ]]
        then
            pushbundle $bundleopts
        fi
    fi
}

CFG="false"
FW="false"

for var in "$@"
do
    case "$var" in
    "pushcfg")
    CFG="pushcfg"
    ;;
    "pushmc")
    FW="pushmc"
    ;;
    *)
    echo "permitted arguments are pushcfg and pushmc"
    ;;
    esac
done

singleton=true
# Exit trap function to clean up
exitTrapFcn()
{
    # Only clean up if we own 'singleton.pfu'
    if $singleton; then
        rm -f /mnt/ramdisk/singleton.pfu
        echo "[action:done]" >> $PFUPROGRESS
    fi
}
trap exitTrapFcn SIGINT SIGTERM EXIT

OTHER_INSTALLED="$(otherinst)"
OTHER_AVAILABLE="false"

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

apiActive $actComp "Performing initial checks."

source "fcns.shinc";  # lib of shell fcns

if [ ! -f $PFULOG ]
then
    touch $PFULOG
fi

# see if the other guy is there and/or is running some kind of codeload
if [ "$OTHER_INSTALLED" == "true" ]
then
    # avoid if getlogs running on the other guy
    rm /mem/other_isGETLOGSrunning
    rpcsys othermcinner "ls /mnt/ramdisk/getlogs.progress >/mem/isGETLOGSrunning"
    RESULT=$?
    if [ "$RESULT" == "0" ]
    then
       rpcget othermcinner /mem/isGETLOGSrunning /mem/other_isGETLOGSrunning
       _flength=$(wc -l /mem/other_isGETLOGSrunning | awk '{ print $1; }')
        if [ $_flength -gt 0 ]
        then
            msg "PFU cannot proceed because the other MC is in the middle of getlogs or managedlogs"
            apiError $actComp 2011 "Other MC doing getlogs or managedlogs."
            exit 0
        fi
    fi

    # we want to see if PFU or CODELOAD is happening on the other guy
    rm /mem/other_isCODELOADrunning
    rpcsys othermcinner "ls /mnt/ramdisk/is.pfu >/mem/isCODELOADrunning; ls /mnt/ramdisk/is.bundle >>/mem/isCODELOADrunning; ls /mnt/ramdisk/codeload.progress >>/mem/isCODELOADrunning"
    RESULT=$?
    if [ "$RESULT" == "0" ]
    then
        rpcget othermcinner /mem/isCODELOADrunning /mem/other_isCODELOADrunning
        _flength=$(wc -l /mem/other_isCODELOADrunning | awk '{ print $1; }')
        if [ $_flength -gt 0 ]
        then
            msg "PFU can not proceed because the other MC is in the middle of codeload or PFU"
            apiError $actComp 2001 "Other MC doing codeload or PFU."
            exit 0
        fi
    else
        msg "PFU can not proceed because the other MC is not up"
        apiError $actComp 2002 "Other MC not up."
        exit 0
    fi
else
    msg "PFU can not proceed because the other MC doesn't exist"
    apiError $actComp 2003 "Other MC not installed."
    exit 0
fi

rpcget othermcinner /cfg/version.txt /tmp/ov.txt
if [ -f /tmp/ov.txt ]
then
    OTHER_AVAILABLE="true"
    rm /tmp/ov.txt
fi

if [ "$OTHER_AVAILABLE" == "false" ]
then
    rpcget othermcinner /app/mc_system_version.txt /tmp/ov.txt
    if [ -f /tmp/ov.txt ]
    then
        OTHER_AVAILABLE="true"
        rm /tmp/ov.txt
    fi
fi

if [ "$OTHER_INSTALLED" != "true" ]
then
    msg "PFU can not proceed because the other controller does not appear to be installed."
    exit 0
fi

if [ "$OTHER_AVAILABLE" != "true" ] && [ "$OTHER_INSTALLED" == "true" ]
then
    msg "PFU can not proceed because the other controller does not appear to be responding to communication attempts."
    exit 0
fi

apiActive $actComp "PFU Started."
msg "PFU Started"
LOCAL_VERSION=$(inicntrl /cfg/version.txt MC_system version);
LOCAL_VERSION_BUNDLE=$(inicntrl /cfg/version.txt system_bundle version);
msg "Local Version: $LOCAL_VERSION"
msg "Local Bundle Version: $LOCAL_VERSION_BUNDLE"
if [ "$LOCAL_VERSION" == "" ]
then
    msg "Could not get local MC version!"
    echo "RETURN_CODE: 11" >> $PFUPROGRESS
        apiError $actComp 2007 "Could not read local MC version."
    exit 1
fi
# handle the case of manufacturing image
_error=$(echo "$LOCAL_VERSION" | grep "Error" | wc -l)
if [ $_error -gt 0 ]
then
    msg "Could not get /cfg/version.txt, using /app/mc_system_version.txt"
    LOCAL_VERSION=$(inicntrl /app/mc_system_version.txt MC_system version);
    msg "Got $LOCAL_VERSION from /app/mc_system_version.txt"
fi

rm /tmp/othermc_version.txt
rpcget othermcinner /cfg/version.txt /tmp/othermc_version.txt
if [ ! -f /tmp/othermc_version.txt ]
then
    rpcget othermcinner /app/mc_system_version.txt /tmp/othermc_version.txt
fi
if [ -f /tmp/othermc_version.txt ]
then
    REMOTE_VERSION=$(inicntrl /tmp/othermc_version.txt MC_system version);
    REMOTE_VERSION_BUNDLE=$(inicntrl /tmp/othermc_version.txt system_bundle version);
    msg "Remote Version: $REMOTE_VERSION"
    msg "Remote Bundle Version: $REMOTE_VERSION_BUNDLE"
    if [ "$REMOTE_VERSION" == "" ]
    then
        msg "Not able to extract version information from /tmp/other_version.txt"
            apiError $actComp 2008 "Could not read remote MC version."
        exit 1
    fi
else
    msg "Not able to get version.txt information from other MC via RPC!"
        apiError $actComp 2009 "Could not get other MC version file."
    exit 1
fi

# also check CPLD versions
apiActive $actComp "Getting CPLD versions."
LOCAL_CPLDversion=""
REMOTE_CPLDversion=""
rm /mem/other_cpldversion
rpcsys othermcinner "cpldrev >/mem/cpldversion"
rpcget othermcinner /mem/cpldversion /mem/other_cpldversion
_flength=$(wc -c /mem/other_cpldversion | awk '{ print $1; }')
if [ $_flength -gt 0 ]
then
    # we have a valid cpldrev for the other guy
    LOCAL_CPLDversion=$(cpldrev)
    REMOTE_CPLDversion=$(cat /mem/other_cpldversion)
    msg "Local CPLD Version: $LOCAL_CPLDversion, Remote CPLD Version: $REMOTE_CPLDversion"
fi
# if we failed to get the other version, both should be set to "", so we will let the MC version dictate the push
if [ \( "$LOCAL_VERSION" != "$REMOTE_VERSION" -o "$LOCAL_CPLDversion" != "$REMOTE_CPLDversion" -o "$LOCAL_VERSION_BUNDLE" != "$REMOTE_VERSION_BUNDLE" \) ] && [[ "pushmc" == "$FW" ]]
then
    msg "Version mismatch, pushing firmware"
else
    msg "Partner versions match local, not pushing firmware"
    FW="false"
fi

if [[ "pushcfg" == "$CFG" ]] || [[ "pushmc" == "$FW" ]]
then
    push2other $FW $CFG
fi

apiOK $actComp
