#!/bin/sh
############################################################
# <bsn.cl fy=2013 v=onl>
#
#        Copyright 2013, 2014 BigSwitch Networks, Inc.
#
# Licensed under the Eclipse Public License, Version 1.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
#        http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific
# language governing permissions and limitations under the
# License.
#
# </bsn.cl>
############################################################
#
# boot
#
# Handle booting SWI image locators.
#
############################################################
set -e
unset testonly help cache

CR="
"

while [ "$1" ]; do
    case "$1" in
        -h|--help)     help=1 ;;
        -t|--testonly) testonly=1 ;;
        --cache)       shift; cache="$1" ;;
        --rootfs)      shift; rootfs="$1" ;;
        *)             break ;;
    esac
    shift
done
SWI=$1

mkdir -p /etc/onl

if [ ! "${SWI}" ] || [ "${help}" ]; then
    cat <<EOF
Usage: $0 [-h|--help] [-t|--testonly] [--rootfs ROOTFS] --cache LOCATION [SWI]

    Loads and boots a software image (SWI).  The load method depends on the
    format of the SWI argument:

    DEV:SWI-PATH
        Mount /dev/DEV to find a SWI
    LABEL-ISH:SWI-PATH
        Mount a filesysem labeled LABEL to find a SWI
    /mnt/DEV/SWI-PATH
    /mnt/onl/LABEL-ISH/SWI-PATH
        Loads a SWI file from local storage device (e.g. flash).
    http://[USER:PASSWORD@]SERVER[:PORT]/SWI-PATH
    ftp://[USER:PASSWORD@]SERVER[:PORT]/SWI-PATH
    ssh|scp://USER:PASSWORD[:PORT]@SERVER/SWI-PATH
    tftp://SERVER[:PORT]/SWI-PATH
        Downloads a SWI file via HTTP, FTP, SSH or TFTP.
    nfs://SERVER[:PORT]/SWI-PATH
        Mounts the parent directory via NFS and loads the SWI file.
    nfs://SERVER[:PORT]/[ROOTFS-PATH/]
        Mounts the parent directory via NFS and uses ROOTFS-PATH
        as the unpacked SWI contents (path must end with '/')
    dir:DEV[:/ROOTFS-PATH]
        Mounts /dev/DEV to find a root filesystem
    dir:LABEL-ISH[:/ROOTFS-PATH]
        Mounts a fileystem labeled LABEL to find a root filesystem
    dir:/mnt/DEV[/ROOTFS-PATH]
    dir:/mnt/onl/LABEL-ISH[/ROOTFS-PATH]
        Mounts a directory on a local storage device to find a root filesystem

EOF
    exit 1
fi

shift

[ ! "${testonly}" ] || set -x

# set up some tempfs for our download

swi_kmin=1048576

workdir=$(mktemp -d -t boot-tmpfs-XXXXXX)

export TMPDIR=$workdir
# export this tempfs as temporary space for swiprep below

echo "creating ${swi_kmin}k of tmpfs in $workdir"
mount -v -t tmpfs -o size=${swi_kmin}k tmpfs $workdir
workmnt=$workdir

do_cleanup() {
    cd /tmp
    if [ "$workmnt" ]; then
        if grep -q "$workmnt" /proc/mounts; then
            umount -v "$workmnt" || :
        fi
    fi
    rm -fr "$workdir"
}
trap "do_cleanup" 0 1

unset swipath host bhost port dir file dev user password scope
case "${SWI}" in
    nfs://*/|dir:*)
        echo "Mounting ${SWI}"

        # do not use the ephemeral temporary directory for
        # locally-mounted directories
        if test "$workmnt"; then
            umount "$workmnt" || :
        fi
        unset TMPDIR

        swipath=$(swimount $SWI)
        if [ "$rootfs" ]; then
            [ -d "${swipath}/${rootfs}" ] || { echo "${SWI}${rootfs} must be an unpacked rootfs"; exit 1; }
        fi
        ;;
    http:*|ftp:*|scp://*|ssh://*|tftp://*|nfs://*)
        echo "Downloading ${SWI}"
        swipath=$(swiget $SWI)
        ;;
    *)
        echo "Locating ${SWI}"
        swipath=$(swiget $SWI)
        ;;
esac
case "$SWI" in
    *::latest)
        swistamp=${SWI%:latest}${swipath##*/}
        ;;
    *)
        swistamp=$SWI
        ;;
esac

echo "SWI=${SWI}" >/etc/onl/boot-config
echo "CONSOLESPEED=$(stty speed)" >>/etc/onl/boot-config
PASSWORD=$(cat /etc/onl/PASSWORD)
[ ! "${PASSWORD}" ] || echo "PASSWORD=${PASSWORD}" >>/etc/onl/boot-config
if [ "${swipath#/tmp/nfs}" != "${swipath}" ]; then
    ifget
    cat /etc/onl/NET >>/etc/onl/boot-config
fi

#
# Cache the SWI if necessary.
#
if [ -n "$cache" ]; then
    python /bin/swicache.py "${swipath}" "${cache}"
fi

if [ "$testonly" ]; then
    echo "swipath=$swipath rootfs=$rootfs"
    echo "Stop here"

    trap "" 0 1
    # leave temporary directory and mounts

    exit 0
fi

if [ -d "${swipath}" ]; then
    # rootfs is a directory
    mkdir -p /newroot
    umount -l /newroot 2>/dev/null || :
    mount --bind "${swipath}/${rootfs}" /newroot
else

    ##############################
    #
    # swiprep will (1) unpack the squashfs image to a file,
    # and (2) extract the filesystem to /newroot.
    #
    # We need to make sure there is enough disk space for this...
    #
    ##############################
    
    set dummy $(df -k -P "$workmnt" | tail -1)
    tmpavail=$5

    # estimate the squashfs size based on the largest one here
    # (there may be more than one arch in the SWI file)
    squashsz=0
    ifs=$IFS; IFS=$CR
    for line in $(unzip -ql "$swipath"); do
        IFS=$ifs
        set dummy $line
        case "$5" in
            *.sqsh)
                if [ "$2" -gt $squashsz ]; then
                    squashsz=$2
                fi
                ;;
        esac
    done
    IFS=$ifs

    # pad by a little to account for inodes and such
    squashsz=$(( $squashsz * 105 / 100 ))

    if [ $squashsz -gt $tmpavail ]; then
        tmpsz=$(( $swi_kmin + $squashsz - $tmpavail ))
        echo "Resizing tmpfs to ${tmpsz}k"
        mount -o remount,size=${tmpsz}k $workmnt
    fi

    swiprep --overlay "${swipath}${rootfs}" --unmount --swiref "$swistamp" /newroot
    swiprep --record "${swipath}${rootfs}" --swiref "$swistamp" /newroot
fi

#
# The file /lib/boot-custom can be provided by customized builds to
# add functionality before the root is switched.
#
if [ -f /lib/boot-custom ]; then
    . /lib/boot-custom
fi

# done with the temporary dirs and mounts
trap "" 0 1
do_cleanup || :

echo "Switching rootfs" # limit 16 chars since serial buffer is not flushed
kill -QUIT 1 # exec /bin/switchroot as PID 1
sleep 30

echo "Boot failed"
exit 1

# Local variables:
# mode: sh
# sh-basic-offset: 4
# sh-indentation: 4
# End:
