#!/bin/bash
#
# Installs a xar-archive of Debian packages.
# Copyright © 2009 - 2019 HP Development Company, L.P.
#

xmlfile="/dev/null"

###############################################################################
usage() {
    exec >&2
    echo "Usage: $(basename $0) [options] file.xar"
    echo "  where options include:"
    echo "    -v|--verbose = increase verbosity level"
    echo "  following options are for internal use only, don't use unless you know what you are doing"
    echo "    --extrdir=dir = assume the xar is already extracted to dir"
    echo "    -s = skip extract and selfupdate, only to be used after a selfupdate"
    echo "    -c = force cleanup after install"
    echo "    -l = force fslock after install"
    exit 1
}

###############################################################################
die() {
    echo "Fatal Error:  $*" >&2
    exit 1
}

###############################################################################
xar_extract() {
    echo "Extracting: $1" >&2

    ########## EXTRACT ##########
    if [ -z "$extrdir" ]; then
	[ $# -ne 1 ] && usage
	xarfile="$1"
	if [ ! -r "$1" ]; then
	    echo "Cannot read requested xar file: '$xarfile'" >&2
	    exit 1
	fi
	xarfile=$(realpath "$xarfile")

	### Temporary working directory.
	MEMTOT=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
	if (( $MEMTOT < 3145728 )); then
		mkdir -p /writable/.tmp
		tmpdir=$(mktemp -d /writable/.tmp/xarinst.XXXXXX)
	else 
		tmpdir=$(mktemp -d /tmp/xarinst.XXXXXX)
	fi
	cd $tmpdir || die "Cannot cd $tmpdir"
	trap "cd /tmp; rm -rf $tmpdir" EXIT

	### Extract from xarfile.
	args="-x"
	[ $verbosity -gt 0 ] && args="$args -v"
	if ! xar $args -f "$xarfile"; then
	    die "Failed to extract contents of xar file '$xarfile'"
	fi

	### Extract the XML descriptor also.
	xmlfile=$(basename "$xarfile" | sed 's/\.xar$/\.xml/')
	xar --dump-toc=$xmlfile -f "$xarfile"
    else
	cd $extrdir || die "Cannot cd $extrdir"
    fi

    echo "$xarfile extracted to $tmpdir" >&2
}

###############################################################################
xar_sizecheck() {
    echo "Sizecheck:" >&2

    ########## CHECK INSTALL SIZE ##########
    INSTALL_SIZE=0
    for package in $(find . -name '*.deb'); do
	packageSize=`dpkg -c $package | awk '{ print $3 }' | paste -sd+ | bc`
	INSTALL_SIZE=$((INSTALL_SIZE + packageSize))
    done

    #echo Expected installed size: $INSTALL_SIZE bytes

    AVAILABLE_SPACE=`df -l /.flash | grep .flash | awk '{ print $4 }'`
    AVAILABLE_SPACE=$((AVAILABLE_SPACE * 1024))
    #echo Available space on disk: $AVAILABLE_SPACE bytes

    if [ "$INSTALL_SIZE" -ge "$AVAILABLE_SPACE" ]; then
	die "Not enough space on disk to install this XAR"
    fi

}

###############################################################################
xar_selfupdate() {
    echo "Selfupdate:" >&2

    ### Unlock file system.
    fsunlock >/dev/null 2>&1
    lockstate=$?
    echo " fslock state: $lockstate" >&2

    RESTART=0
    [ -e $tmpdir/THINPRO/selfupdate ] && . $tmpdir/THINPRO/selfupdate

    return $RESTART
}

###############################################################################
xar_preinstall() {
    echo "Preinstall:" >&2

    [ -e $tmpdir/THINPRO/preinst ] && . $tmpdir/THINPRO/preinst
}

###############################################################################
xar_v1install() {
    echo "Running v1 Install:" >&2

    ### Install debian packages.
    dpkg --install --no-force-downgrade --no-debsig \
        --force-confdef --force-confnew --force-confmiss \
        $(find . -name '*.deb' | sort)
    return $?
}

###############################################################################
xar_v2install() {
    echo "Running v2 Install:" >&2
    echo "  XAR_APT_FULLCMD=$XAR_APT_FULLCMD" >&2
    echo "  XAR_APT_OPTIONS=$XAR_APT_OPTIONS" >&2
    echo "  XAR_APT_PACKAGES=$XAR_APT_PACKAGES" >&2

    retval=0
    if [ "$XAR_APT_FULLCMD" != "" ] ; then
        $XAR_APT_FULLCMD || retval=$?
        apt-get check || retval=$?
        return $retval
    fi

    # guess package name, may not be accurate
    pkgname=`ls $tmpdir | grep -v THINPRO`
    pkgname=`basename $pkgname | sed "s/_/-/g"`
    pkgname="hptc-sp-$pkgname"

    # backup source.list file
    mv /etc/apt/sources.list /etc/apt/sources.list.backedbyxar
    echo "deb file://$tmpdir/repo $pkgname main non-free" > /etc/apt/sources.list

    mkdir -p $tmpdir/repo/conf || die "Cannot mkdir $tmpdir/$spname"
    cat > $tmpdir/repo/conf/distributions << EOF
Codename: $pkgname
Suite: $pkgname
Components: main non-free
Architectures: i386 amd64
Origin: HP
Description: A service pack repository for the HP Thin Client.

EOF

    # build a temp repo
    debs="$(find . -name '*.deb' | sort)"
    yes | reprepro -b $tmpdir/repo includedeb $pkgname $debs

    rm -rf $debs

    apt-get update || retval=$?
    apt-get upgrade --with-new-pkgs --force-yes -y || retval=$?

    aptoptions="--reinstall --force-yes -y "
    aptpackages="$pkgname"
    [ "$XAR_APT_OPTIONS" != "" ] && aptoptions=$XAR_APT_OPTIONS
    [ "$XAR_APT_PACKAGES" != "" ] && aptpackages=$XAR_APT_PACKAGES
    apt-get install $aptoptions $aptpackages || retval=$?
    apt-get check || retval=$?

    # restore source.list file
    mv /etc/apt/sources.list.backedbyxar /etc/apt/sources.list 

    return $retval
}

###############################################################################
xar_install() {
    if [ -e $tmpdir/THINPRO/xarinstall.v2 ] ; then
	xar_v2install
        return $?
    else
	xar_v1install
        return $?
    fi
}

###############################################################################
xar_postinstall() {
    echo "Postinstall:" >&2

    if [ -e $tmpdir/THINPRO/postinst ]; then
        . $tmpdir/THINPRO/postinst
        return $?
    fi
}

###############################################################################
xar_cleanup() {
    echo "Cleanup:" >&2

    cd /
    [ $clean -eq 1 ] && rm -rf $tmpdir

    ### Re-lock the file system.
    [ $lockstate -eq 0 ] && fslock >/dev/null 2>&1

}

###############################################################################
###############################################################################
### Parse command line
#echo "Running with $*" >&2
xarinstall=$(realpath $0)
[ -e "$xarinstall" ] || xarinstall=`which xarinstall`
cmdline=$*
verbosity=0
extrdir=""
clean=1
skip=0
lockstate=1
while [ $# -gt 0 ]; do
    case "$1" in
        --extrdir) extrdir=$2; 
		tmpdir=$(realpath $extrdir 2>/dev/null)
		clean=0
		shift
		;;
        --extrdir=*) extrdir=${1##--extrdir=}
		tmpdir=$(realpath $extrdir 2>/dev/null)
		clean=0
		;;
        -c) clean=1;;
        -s) skip=1;;
        -l) lockstate=0;;
        -v|--verbose) verbosity=$((verbosity+1)); set -x;
		;;
	-*) echo ${1}; usage;
		;;
	*) break;;
    esac
    shift 1
done

if [ $skip -eq 0 ]; then
    xar_extract "$1"
    xar_selfupdate
    if [ $? -ne 0 ]; then
        echo "Restarting xarinstall after self-update..." >&2
        # rebuild cmdline
        args=$cmdline
        [ $lockstate -eq 0 ] && args="-l $args"
        [ "x$extrdir" == "x" ] && args="--extrdir=$tmpdir -c $args"  || args="$args --extrdir=$tmpdir"
        exec /bin/bash $xarinstall -s $args
    fi
fi

### Do not continue without support license unless the package
### has the show_if_unlicensed element set to true.
license_state=$(mclient --quiet get tmp/license/state 2>/dev/null)
[ -z "$license_state" ] && license_state=7
if [ $license_state -gt 1 ]; then
    show=$(xmlstarlet sel -t \
	-m "/xar/subdoc[@subdoc_name='service-pack-description']" \
	-v //show_if_unlicensed -n $xmlfile)
    if [ "$show" != "true" ]; then
    	logger -s -p user.warning -t xarinstall \
	    "Cannot run xarinstall while unlicensed! ($license_state)"
	xar_cleanup
	exit 1
    fi
fi

xar_sizecheck
xar_preinstall
xar_install || retval=$?
xar_postinstall || retval=$?
xar_cleanup

### Done.
exit $retval
