#! /bin/bash
#   pbuilder -- personal Debian package builder
#   Copyright © 2001-2007 Junichi Uekawa <dancer@debian.org>
#               2015-2016 Mattia Rizzolo <mattia@debian.org>
#
#   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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
###############################################################################


# This code is called for pbuilder and pbuilder inside pbuilder-uml;
# pbuilder-uml calls uml-checkparams.

export PBUILDER_PKGLIBDIR="${PBUILDER_PKGLIBDIR:-$PBUILDER_ROOT/usr/lib/pbuilder}"
CMDLINE="$@"

. "$PBUILDER_PKGLIBDIR"/pbuilder-loadconfig
. "$PBUILDER_PKGLIBDIR"/pbuilder-modules

#default value for this option is !empty!
INTERNAL_BUILD_UML=""
TWICE=""
CHROOTEXEC=""
OVERRIDE_APTLINES="no"
OVERRIDE_APTLINES_WARN="" # set this if --override-config option should be set.
BINARY_ARCH="any"  # can be one of 'any', 'all', 'binary'
BIN_NMU="no"
PBUILDER_BUILD_LOGFILE=
PRESERVE_BUILDPLACE="no"
unset EXTRA_CONFIGFILE || true
PBUILDER_DEBUGMODE=
SAVE_AFTER_LOGIN=
#option for user-mode-linux only.
IGNORE_UMOUNT=""

while [ -n "$1" ]; do
    case "$1" in
    --basetgz)
        if [ "$PBUILDER_OPERATION" = create ]; then
                touch "$2"
        fi
        if [ ! -f "$2" ]; then
            log.e "File $2 does not exist"
            exit 1
        fi
        BASETGZ=$(readlink -f "$2");
        shift; shift;
        ;;
    --buildplace)
        if [ ! -d "$2" ] ; then
            log.e "Directory $2 does not exist"
            exit 1
        fi
        BUILDPLACE=$(readlink -f "$2");
        shift; shift;
        ;;
    --mirror)
        MIRRORSITE="$2";
        OVERRIDE_APTLINES_WARN=yes
        shift; shift;
        ;;
    --othermirror)
        OTHERMIRROR="$2";
        OVERRIDE_APTLINES_WARN=yes
        shift; shift;
        ;;
    --http-proxy)
        export http_proxy="$2";
        shift; shift;
        ;;
    --use-network)
        USENETWORK="$2"
        shift 2
        ;;
    --use-cgroup)
        USECGROUP="$2"
        shift 2
        ;;
    --distribution)
        DISTRIBUTION="$2";
        OVERRIDE_APTLINES_WARN=yes
        shift; shift;
        ;;
    --architecture)
        ARCHITECTURE="$2";
        shift; shift;
        ;;
    --host-arch)
        HOST_ARCH="$2"
        shift 2
        ;;
    --no-auto-cross)
        if [ "$NO_AUTO_CROSS" = "yes" ]; then
            NO_AUTO_CROSS="really-dont-mess-with-me"
        else
            NO_AUTO_CROSS=yes
        fi
        shift
        ;;
    --components)
        COMPONENTS="$2";
        OVERRIDE_APTLINES_WARN=yes
        shift; shift;
        ;;
    --buildresult)
        if [ -n "$2" ]; then
            if [ -d "$2" ]; then
                BUILDRESULT=$(readlink -f "$2");
            else
                BUILDRESULT="$2"
                log.w "Build-result Directory $2 does not exist"
                # warn, but make it progress.
            fi
        else
            BUILDRESULT=
        fi
        shift; shift;
        ;;
    --compressprog)
        if [ -n "$2" ]; then
            COMPRESSPROG="$2"
        fi
        shift; shift;
        ;;
    --aptcache)
        if [ -n "$2" ]; then
        if [ -d "$2" ]; then
            APTCACHE=$(readlink -f "$2");
        else
            log.e "Directory $2 does not exist"
            exit 1
        fi
        else
        APTCACHE=
        fi
        shift; shift;
        ;;
    --autocleanaptcache)
        AUTOCLEANAPTCACHE=yes
        shift;
        ;;
    --removepackages)
        REMOVEPACKAGES="$2";
        shift; shift;
        ;;
    --loglevel)
        LOGLEVEL="$2"
        shift; shift;
        ;;
    --configfile)
        if [ ! -f "$2" ]; then
            log.e "Config file $2 does not exist"
            exit 1
        fi
        . "$2";
        # deprecated in v0.216
        if [ -n "$PKGNAME_LOGFILE_EXTENTION" ] ; then
            echo "W: The configuration option PKGNAME_LOGFILE_EXTENTION is deprecated in favour of PKGNAME_LOGFILE_EXTENSION. Though, while you have the former set it'll take precedence over the latter."
            echo "W: PKGNAME_LOGFILE_EXTENTION will be removed at some point, please update your config!"
            PKGNAME_LOGFILE_EXTENSION="$PKGNAME_LOGFILE_EXTENTION"
        fi
        EXTRA_CONFIGFILE[${#EXTRA_CONFIGFILE[@]}]="$2";
        shift; shift;
        ;;
    --extrapackages)
        EXTRAPACKAGES="${EXTRAPACKAGES:+$EXTRAPACKAGES }$2";
        shift; shift;
        ;;
    --hookdir)
        HOOKDIR="$2";
        shift; shift;
        ;;
    --debemail)
        DEBEMAIL="$2";
        shift; shift;
        # deprecated in v0.222
        if [ -n "$DEBEMAIL" ]; then
            echo "W: The configuration option DEBEMAIL is deprecated.  Please pass the required options to DEBBUILDOPTS (--debbuildopts command line flag) instead."
            echo "W: DEBEMAIL='$DEBEMAIL' is ignored!"
        fi
        ;;
    --debbuildopts)
        # append to DEBBUILDOPTS or reset to empty if $2 isn't set
        DEBBUILDOPTS="${2:+${DEBBUILDOPTS:+$DEBBUILDOPTS }$2}";
        shift; shift;
        ;;
    --profiles)
        export DEB_BUILD_PROFILES="${2//,/ }"
        shift 2
        ;;
    --logfile)
        log.i "Logging to $2"
        exec > >(tee "$2") 2>&1
        PBUILDER_BUILD_LOGFILE=$(readlink -f "$2")
        shift; shift;
        ;;
    --pkgname-logfile)
        PKGNAME_LOGFILE="yes"
        shift;
        ;;
    --aptconfdir)
        APTCONFDIR="$2";
        shift; shift;
        ;;
    --timeout)
        TIMEOUT_TIME="$2"
        shift; shift;
        ;;
    --help)
        showhelp
        exit 0
        ;;
    --override-config)
        OVERRIDE_APTLINES="yes"
        shift;
        ;;
    --binary-arch)
        BINARY_ARCH="binary"
        # XXX this might be overwriten by --debbuildopts ""
        DEBBUILDOPTS="${DEBBUILDOPTS} -B"
        shift;
        ;;
    --binary-indep)
        BINARY_ARCH="all"
        # XXX this might be overwritten by --debbuildopts ""
        DEBBUILDOPTS="${DEBBUILDOPTS} -A"
        shift
        ;;
    --source-only-changes)
        SOURCE_ONLY_CHANGES="yes"
        shift;
        ;;
    --no-source-only-changes)
        SOURCE_ONLY_CHANGES="no"
        shift
        ;;
    --bin-nmu)
        BIN_NMU="yes"
        BINARY_ARCH="binary"
        # XXX this might be overwriten by --debbuildopts ""
        DEBBUILDOPTS="${DEBBUILDOPTS} -B"
        BINNMU_MESSAGE="$2"
        shift; shift;
        ;;
    --bin-nmu-maintainer)
        BINNMU_MAINTAINER="$2"
        shift; shift;
        ;;
    --bin-nmu-version)
        BINNMU_VERSION="$2"
        shift; shift;
        ;;
    --bin-nmu-timestamp)
        BINNMU_TIMESTAMP="$2"
        shift 2
        ;;
    --preserve-buildplace)
        PRESERVE_BUILDPLACE="yes"
        shift;
        ;;
    --bindmounts)
        BINDMOUNTS="${BINDMOUNTS} $2"
        shift; shift;
        ;;
    --debootstrapopts)
        # specify this option to set --variant=buildd value to debootstrap
        DEBOOTSTRAPOPTS[${#DEBOOTSTRAPOPTS[@]}]="$2";
        shift; shift;
        ;;
    --debootstrap)
        # Use this option to specify debootstrap/cdebootstrap
        DEBOOTSTRAP="$2";
        shift; shift;
        ;;
    --allow-untrusted)
        ALLOWUNTRUSTED=yes;
        shift;
        ;;
    --debdelta)
        DEBDELTA=yes;
        shift;
        ;;
    --keyring)
        APTKEYRINGS[${#APTKEYRINGS[@]}]="$2";
        shift; shift;
        ;;
    --save-after-login|--save-after-exec)
        SAVE_AFTER_LOGIN=yes;
        shift;
        ;;
    --inputfile)
        if [ ! -f "$2" ]; then
            log.e "Input file $2 does not exist"
            exit 1
        fi
        INPUTFILE[${#INPUTFILE[@]}]="$2";
        shift; shift;
        ;;
    --outputfile)
        OUTPUTFILE[${#OUTPUTFILE[@]}]="$2";
        shift; shift;
        ;;

    ## internal options.
    --internal-chrootexec)
        # specify custom chrootexec function -- this is internal debugging function
        CHROOTEXEC="$2"
        shift; shift;
        ;;
    --debug)
        PBUILDER_DEBUGMODE=yes
        set -x
        shift;
        ;;
    --no-targz)
        # specify this option if I am running in no-targz mode
        log.i "Running in no-targz mode"
        INTERNAL_BUILD_UML="yes"
        shift;
        ;;
    --internal-build-uml)
        # specify this option if I am running inside UML.
        log.i "Running in pbuilder-user-mode-linux mode"
        INTERNAL_BUILD_UML="yes"
        IGNORE_UMOUNT="no"
        shift;
        ;;
    --twice)
        TWICE="yes"
        shift;
        ;;
    --) # end of processing for this
        shift;
        break;
        ;;
    --*)
        log.e "Unknown option [$1] was specified "
        exit 1;
        ;;
    *)
        break;
        ;;
  esac
done

log.d "cmdline: ${PBUILDER_OPERATION} ${CMDLINE}"

BUILDPLACE=${BUILDPLACE?"Build root directory is not defined"}

# the default is to add a PID in the buildplace specified in the config file.
BASEBUILDPLACE="$BUILDPLACE"
if [ "${INTERNAL_BUILD_UML}" != "yes" -a "${PRESERVE_BUILDPLACE}" != "yes" ]; then
    BUILDPLACE="$BUILDPLACE/$$"
fi

# sanity check of LOGLEVEL
case "$LOGLEVEL" in
    D|I|W|E) ;;
    *)  # use log() instead of log.e() to override the buggy LOGLEVEL
        log "E: A non-valid LOGLEVEL has been specified: '${LOGLEVEL}'."
        log "E: Valid values are D, I, W, E"
        exit 1
        ;;
esac

# set up CHROOTEXEC
if [ -z "${CHROOTEXEC}" ]; then
    CHROOTEXEC="chroot $BUILDPLACE "
    if [ "$EATMYDATA" = "yes" ]; then
        if /sbin/ldconfig -p | grep libeatmydata >/dev/null 2>&1 ; then
            if ! which eatmydata > /dev/null 2>&1 ; then
                log.w "the eatmydata binary is not installed on the host, not using it."
                EATMYDATA=not-available
            fi
        else
            log.w "libeatmydata is not installed on the host, not using it."
            EATMYDATA=not-available
        fi
    fi
    if [ "$USECGROUP" = "yes" ]; then
        # v215 is required for systemd-escape
        if systemctl is-system-running --quiet >/dev/null 2>&1 && \
                dpkg --compare-versions "$(dpkg-query -W --showformat='${Version}' systemd)" gt 215; then
            # --description uses that no-spaces string because the quoting sucks
            # right now, and it would end up trying to execute $PBUILDER_OPERATION…
            # long-term solution is to turn $CHROOTEXEC into a command and properly
            # use arrays instead of plain strings.
            SYSTEMD_SLICE="system-pbuilder-${PBUILDER_OPERATION}${1:+-"$(systemd-escape "$(basename "$1" .dsc)")"}-$$.slice"
            systemctl_run=(
                systemd-run
                --quiet
                --scope
                --description="pbuilder_${PBUILDER_OPERATION}${1:+_"$(basename "$1")"}"
                --slice="$SYSTEMD_SLICE"
            )
            CHROOTEXEC="${systemctl_run[*]} $CHROOTEXEC"
        else
            log.w "cgroups are not available on the host, not using them."
            USECGROUP=not-available
        fi
    fi
fi

# handle 'experimental' specially. -- required for raw pbuilder (create/update) only.
case "$PBUILDER_OPERATION" in
    update|create)
        if [ "$DISTRIBUTION" = "experimental" ]; then
            DISTRIBUTION="sid"
            EXPERIMENTAL="true"
        else
            EXPERIMENTAL=""
        fi
        ;;
    *) EXPERIMENTAL="" ;;
esac


case "$PBUILDER_OPERATION" in
    login|execute|pdebuild)
        # don't do anything if it is "login", or pdebuild
        ;;
    *)
        # line from kobras@debian.org
        if [ "$DEBIAN_FRONTEND" = "noninteractive" -o "$DEBIAN_FRONTEND" = "Noninteractive" ]; then
            exec < /dev/null
            # set variables used in the upgrade option.
            FORCE_CONFNEW[0]='-o'
            FORCE_CONFNEW[1]='DPkg::Options::=--force-confnew'
        else
            unset FORCE_CONFNEW || true
        fi
        ;;
esac

if [ -n "$CCACHEDIR" ]; then
    if [ -d "$CCACHEDIR" ]; then
        BINDMOUNTS="$BINDMOUNTS $CCACHEDIR"
    fi
    export PATH="/usr/lib/ccache:$PATH"
fi

# sort BINDMOUNTS to ensure that deeper directories are mounted last
BINDMOUNTS="$(for i in $BINDMOUNTS; do echo $i; done | sort -u)"

if [ "$ALLOWUNTRUSTED" = "yes" ]; then
    PBUILDERSATISFYDEPENDSOPT[${#PBUILDERSATISFYDEPENDSOPT[@]}]='--allow-untrusted'
    # Also duplicated in pbuilder-satisfydepends-checkparams!
    # apt flag to accept untrusted packages
    APTGETOPT[${#APTGETOPT[@]}]='--force-yes'
    # aptitude flag to accept untrusted packages
    APTITUDEOPT[${#APTITUDEOPT[@]}]='-o'
    APTITUDEOPT[${#APTITUDEOPT[@]}]='Aptitude::CmdLine::Ignore-Trust-Violations=true'
fi
