#! /bin/bash
# common modules for pbuilder.
#   pbuilder -- personal Debian package builder
#   Copyright (C) 2001-2003 Junichi Uekawa
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

set -e

function cleanup_function () {
    rm -f ${INSIDE_PBUILDER}
    ${EXTRACLEANUP}
}


. /usr/lib/pbuilder/pbuilder-loadconfig

for SYSTEM_CONFIG in /usr/share/pbuilder/pbuilder-uml.conf /etc/pbuilder/pbuilder-uml.conf ${HOME}/.pbuilderrc; do
    if [ -f ${SYSTEM_CONFIG} ]; then
	. ${SYSTEM_CONFIG}
    fi
done

function usecow () {
    PBUILDER_COWFILENAME="${BUILDPLACE}/$$.cow"
    PBUILDER_COW="${PBUILDER_COWFILENAME},"
    if ! touch "${PBUILDER_COWFILENAME}"; then
	echo "E: Cannot write-access to the COW file, check buildplace"
	exit 1;
    fi
    rm -f "${PBUILDER_COWFILENAME}"
}

function cleancow () {
    # clean the cow file
    rm -f "${PBUILDER_COWFILENAME}"
}

function operate_uml () {
    # opeartes on UML, and runs pbuilder $1
    # use this script file to bootstrap the pbuilder inside the UML
    INSIDE_PBUILDER=$(tempfile)
    trap cleanup_function exit
    UML_CHROOT_MOUNTPOINT=/var/cache/pbuilder/pbuilder-mnt
    UML_CHROOT_BUILDRESULTMOUNTDIR=/var/cache/pbuilder/pbuilder-umlresult
#The following script is ran inside UML as soon as it is started.

    cat <<EOF > ${INSIDE_PBUILDER}
#! /bin/bash
${UML_DEBUGMODE}

mount -t proc /proc /proc
mount -t tmpfs /tmp /tmp
#if ! fsck -p /dev/ubd/1; then
#  echo "W: Failed to fsck /dev/ubd/1"
#fi
if ! mount -t ext2 /dev/ubd/1 ${UML_CHROOT_MOUNTPOINT}; then
  echo "E: Cannot mount /dev/ubd/1, is UML root image accessible as current user?"
  exit 1
fi
export LOGNAME="${LOGNAME}"
export HOME="${HOME}"
hostname "${UML_HOSTNAME}"
if [ "${UML_MOUNT_TMPFS}" = "yes" ]; then
  mount -t tmpfs ${UML_CHROOT_MOUNTPOINT}/tmp ${UML_CHROOT_MOUNTPOINT}/tmp
else
  # clean up tmp before playing with it.
  rm -rf ${UML_CHROOT_MOUNTPOINT}/tmp
  mkdir ${UML_CHROOT_MOUNTPOINT}/tmp
  chmod 1777 ${UML_CHROOT_MOUNTPOINT}/tmp
fi

cat <<IP > ${UML_CHROOT_MOUNTPOINT}/etc/network/interfaces
auto lo
iface lo inet loopback

# The first network card - this entry was created during the Debian installation
auto eth0
iface eth0 inet static
    address $UML_IP
    netmask $UML_NETMASK
    network $UML_NETWORK
    broadcast $UML_BROADCAST
    gateway $UML_GATEWAY

IP
cat <<SHELL > ${UML_CHROOT_MOUNTPOINT}/tmp/chrootshell
#! /bin/bash
${UML_DEBUGMODE}
# the shell executed inside chroot inside UML
    echo Starting network inside the chroot
    /etc/init.d/networking stop
    /etc/init.d/networking start
SHELL
chmod a+x ${UML_CHROOT_MOUNTPOINT}/tmp/chrootshell
chroot ${UML_CHROOT_MOUNTPOINT} /tmp/chrootshell

#some variables need to be set from outside values, possibly
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
export TMPDIR=/tmp
unset EXTRAOPT || true
declare -a EXTRAOPT
if [ -n "${UML_DISTRIBUTION}" ]; then
  EXTRAOPT[0]="--distribution"
  EXTRAOPT[1]="${UML_DISTRIBUTION}"
fi
if [ -z "${UML_BUILDRESULT}" ]; then
  BUILDRESULT=
else
  echo " -> Mount build result dir outside UML [${UML_BUILDRESULT}] as [${UML_CHROOT_BUILDRESULTMOUNTDIR}]"
  BUILDRESULT=${UML_CHROOT_BUILDRESULTMOUNTDIR}
  mkdir -p ${UML_CHROOT_BUILDRESULTMOUNTDIR} || true
  mount -t hostfs none "${UML_CHROOT_BUILDRESULTMOUNTDIR}" -o "${UML_BUILDRESULT}"
fi
pbuilder "$1" ${UML_EXTRAOPT} \${EXTRAOPT[@]} --buildresult "\${BUILDRESULT}" --buildplace "${UML_CHROOT_MOUNTPOINT}" --internal-build-uml ${BUILDING_DSC_FILE}  ${UML_EXECUTE_EXTRAOPT}
echo \$? > /proc/exitcode
EOF

    chmod a+x ${INSIDE_PBUILDER}

    if [ $( id -u ) = 0 ]; then
	echo "W: You are uid=0. Don't run UML as uid=0, it's not supported."
    fi

    echo Invoking: "linux mem=${UML_MEM} eth0=${MY_ETH0} con0=fd:0,fd:1 con=pty root=/dev/root rootflags=/ rootfstype=hostfs ubd1=${PBUILDER_COW}${PBUILDER_UML_IMAGE} devfs=mount init=${INSIDE_PBUILDER} rw"
    if linux mem=${UML_MEM} eth0=${MY_ETH0} con0=fd:0,fd:1 con=pty root=/dev/root rootflags=/ rootfstype=hostfs ubd1="${PBUILDER_COW}${PBUILDER_UML_IMAGE}" devfs=mount init=${INSIDE_PBUILDER} rw ; then
	UML_EXITCODE=$?
	echo " -> Successful exit from user-mode linux"
    else
	UML_EXITCODE=$?
	echo " -> Exit code ${UML_EXITCODE}"
    fi
}

PBUILDER_COW=""
OPERATION="$1"
UML_EXTRAOPT=
UML_DEBUGMODE=
UML_EXECUTE_EXTRAOPT=
UML_LOGIN_NOCOW=
shift;

while [ -n "$1" ] ; do
    case "$1" in
	--eth0)
	    MY_ETH0="$2";
	    shift; shift;
	    ;;
	--uml-ip)
	    UML_IP="$2";
	    shift; shift;
	    ;;
	--uml-netmask)
	    UML_NETMASK="$2";
	    shift; shift;
	    ;;
	--uml-network)
	    UML_NETWORK="$2";
	    shift; shift;
	    ;;
	--uml-broadcast)
	    UML_BROADCAST="$2";
	    shift; shift;
	    ;;
	--uml-gateway)
	    UML_GATEWAY="$2";
	    shift; shift;
	    ;;
	--uml-image)
	    PBUILDER_UML_IMAGE="$2";
	    shift; shift;
	    ;;
	--mount-tmpfs)
	    UML_MOUNT_TMPFS="$2";
	    shift; shift;
	    ;;
	--uml-mem)
	    UML_MEM="$2";
	    shift; shift;
	    ;;
	--uml-hostname)
	    UML_HOSTNAME="$2";
	    shift; shift;
	    ;;
	--uml-debugmode)
	    UML_DEBUGMODE="set -x";
	    shift;;
	--distribution)
	    UML_DISTRIBUTION="$2";
	    shift; shift;
	    ;;
	--uml-login-nocow)
	    UML_LOGIN_NOCOW=yes;
	    shift;
	    ;;
	#things that can be passed through without options
	--override-config|--binary-arch)
	    UML_EXTRAOPT="${UML_EXTRAOPT} $1"
	    shift;;
	--configfile)
	    UML_EXTRAOPT="${UML_EXTRAOPT} $1 $2"
	    . "$2"
	    shift; shift;;
	#things that can be passed through with options
	--timeout|--http-proxy|--hookdir|--aptconfdir|--bindmounts|--mirror|--nonusmirror)
	    UML_EXTRAOPT="${UML_EXTRAOPT} $1 $2"
	    shift; shift;;
	--buildresult)
	    # ignore buildresult
	    if [ -d "$2" ]; then
		UML_BUILDRESULT=$(readlink -f "$2")
	    else
		echo "E: Directory $2 does not exist" >&2
		exit 1
	    fi
	    shift; shift;;
	--buildplace)
	    if [ -d "$2" ]; then
		BUILDPLACE=$(readlink -f "$2")
	    else
		echo "E: Directory $2 does not exist" >&2
		exit 1
	    fi
	    shift; shift;; 
	--logfile)
	    exec > "$2";
	    exec 2>&1
	    PBUILDER_BUILD_LOGFILE=$(readlink -f "$2")
	    shift;shift;;
	--)
	    shift;
	    break;
	    ;;
	--*)
	    echo "Error: Unknown option [$1] was specified " >&2 
	    exit 1;
	    ;;
	*)
	    break;
	    ;;
    esac
done

if ! touch "${BUILDPLACE}/touch" ; then
    echo "E: Cannot touch BUILDPLACE/touch, check BUILDPLACE" >&2
    exit 1
fi

BUILDING_DSC_FILE=$(readlink -f "$1") || true # ignore failure here
EXTRACLEANUP=
UML_EXITCODE=1
case "${OPERATION}" in
    build)
	usecow
	EXTRACLEANUP=cleancow
	operate_uml build 
	;;
    update)
	operate_uml update
	;;
    login)
	if [ "${UML_LOGIN_NOCOW}" = "yes" ]; then
	    echo " -> Not using COW filesystem for login session, modifications to this session will persist"
	else
	    usecow
	    EXTRACLEANUP=cleancow
	fi
	operate_uml login
	;;
    execute)
	usecow
	EXTRACLEANUP=cleancow
	shift;
	UML_EXECUTE_EXTRAOPT="${UML_EXECUTE_EXTRAOPT} $@"
	operate_uml execute 
	;;
    create)
	if [ -n "${UML_DISTRIBUTION}" ]; then
	    if [ ! -d ~/.pbuilder-user-mode-linux ]; then
		mkdir ~/.pbuilder-user-mode-linux
	    fi
	    sed "s/dist=.*/dist=${UML_DISTRIBUTION}/" /etc/rootstrap/rootstrap.conf >  ~/.pbuilder-user-mode-linux/rootstrap.conf
	    if [ -f rootstrap.conf ]; then
		sed "s/dist=.*/dist=${UML_DISTRIBUTION}/" rootstrap.conf >  ~/.pbuilder-user-mode-linux/rootstrap.conf
	    fi
	    cd ~/.pbuilder-user-mode-linux
	fi
	rootstrap -s ${ROOTSTRAP_IMAGESIZE:=1000} ${PBUILDER_UML_IMAGE}
	operate_uml update
	;;
    *)
	echo "Error: Unknown option [${OPERATION}] was specified " >&2 
	;;
esac

exit ${UML_EXITCODE}
