#!/bin/sh
# Run various programs to select packages to install.
set -e

cleanup () {
	rm -f /usr/bin/scrollkeeper-update /usr/bin/scrollkeeper-rebuilddb
	dpkg-divert --package base-config --rename --quiet --remove \
		/usr/bin/scrollkeeper-update
	dpkg-divert --package base-config --rename --quiet --remove \
		/usr/bin/scrollkeeper-rebuilddb
	rm -f /etc/apt/apt.conf.d/99base-config
}

case "$1" in
''|new)
	# Create temporary config and templates databases for use by the
	# debconf instance responsible for displaying the progress bar. This
	# allows it to avoid locking the normal databases, which will be
	# needed by other debconf instances while installing packages.
	#
	# Note that nothing written to these databases will be preserved
	# when this script exits.
	debconf-copydb configdb progressconfigdb \
		-c Name:progressconfigdb -c Driver:File \
		-c Filename:"$BC_TMPDIR/progressconfig.dat"
	# ignore errors for now; see bug #321290
	debconf-copydb templatedb progresstemplatedb \
		-c Name:progresstemplatedb -c Driver:File \
		-c Filename:"$BC_TMPDIR/progresstemplates.dat" 2>/dev/null
	cat >"$BC_TMPDIR/progress.conf" <<EOF
Config: configdb
Templates: templatedb

Name: configdb
Driver: File
Mode: 600
Filename: $BC_TMPDIR/progressconfig.dat

Name: templatedb
Driver: File
Mode: 644
Filename: $BC_TMPDIR/progresstemplates.dat
EOF

	# Make sure /etc/cron.daily/apt doesn't stomp over the apt
	# cache while we're running.
	cat >/etc/apt/apt.conf.d/99base-config <<EOF
APT::Periodic::Update-Package-Lists "0";
APT::Periodic::Download-Upgradeable-Packages "0";
APT::Periodic::AutocleanInterval "0";
APT::Archives::MaxAge "0";
APT::Archives::MinAge "0";
APT::Archives::MaxSize "0";
EOF

	DEBCONF_SYSTEMRC="$BC_TMPDIR/progress.conf" NEW="$1" \
		$0 debconf 2>/var/log/base-config-pkgsel.log
	;;
debconf)
	. /usr/share/debconf/confmodule

	unset DEBCONF_SYSTEMRC # subprocesses don't want this

	APT_OPTS='-o APT::Status-Fd=4 -o APT::Keep-Fds::=5 -o APT::Keep-Fds::=6'

	# Wrap a subprocess such that it will use the debconf passthrough
	# frontend.
	passthrough () {
		# The passthrough frontend doesn't yet know how to fetch its
		# priority from the UI agent. Work around this.
		db_get debconf/priority
		priority="$RET"

		DEBIAN_HAS_FRONTEND= DEBCONF_REDIR= \
		DEBIAN_FRONTEND=passthrough DEBIAN_PRIORITY="$priority" \
		DEBCONF_READFD=5 DEBCONF_WRITEFD=6 \
			"$@" 5<&0 6>&3 3>&- </dev/null
	}

	# Wrap package installation in a progress bar. The nominated
	# subprocess should output apt install-status information to fd 4,
	# and should ensure to keep fds 5 and 6 open when invoking debconf
	# (directly or indirectly), as those fds will be used for the
	# debconf passthrough protocol.
	progress () {
		MIN="$1"
		MAX="$2"
		shift 2
		(if passthrough "$@" 4>&1 >&2; then
			:
		else
			# Propagate the exit status to the last process in
			# the pipeline so that our caller can see it.
			echo "_error:$?"
			exit "$?"
		fi | while IFS=: read -r status pkg percent description; do
			# Crude waypointing. 15% was chosen to match
			# base-installer, but could benefit from timing
			# tests under various bandwidth conditions.
			case $status in
				dlstatus)
					min=0
					len=15
					;;
				pmstatus)
					min=15
					len=85
					;;
				_error)
					exit "$pkg"
					;;
				*)	continue ;;
			esac
			percent="${percent%%.*}"
			percent="$(($percent * $len / 100 + $min))"
			position="$(($percent * ($MAX - $MIN) / 100 + $MIN))"
			db_progress SET "$position" <&4
			db_subst base-config/progress/info DESCRIPTION \
				"$description" <&4
			db_progress INFO base-config/progress/info <&4
		done) 4<&0
	}

	failure () {
		# This function is reached if there was some problem installing.
		# It uses debconf to explain the failure to the user.
		db_settitle base-config/title
		db_fset base-config/install-problem seen false
		db_input critical base-config/install-problem || true
		db_go || true
		if [ -f /etc/apt/sources.list.apt-setup ]; then
			mv -f /etc/apt/sources.list.apt-setup /etc/apt/sources.list
			apt-get -o Acquire::gpgv::Options::=--ignore-time-conflict update || true
		fi
	}

	# Make dpkg not background itself to get a shell.
	export DPKG_NO_TSTP="yes"

	db_progress START 0 1000 base-config/progress/title

	# scrollkeeper takes forever, so we divert it for the duration.
	dpkg-divert --package base-config --rename --quiet --add \
		/usr/bin/scrollkeeper-update
	dpkg-divert --package base-config --rename --quiet --add \
		/usr/bin/scrollkeeper-rebuilddb
	ln -sf /bin/true /usr/bin/scrollkeeper-update
	ln -sf /bin/true /usr/bin/scrollkeeper-rebuilddb

	# Check for known X servers. apt-cache will only fail if
	# none of the packages exist.
	if apt-cache show xserver-xfree86 xserver-xorg >/dev/null 2>&1; then
		# X needs some packages installed before its debconf config is run
		# to make it do hardware autodetection. The only way to make sure
		# these are installed properly is to install them now, before packages
		# are selected. This way, even if the user picks X in
		# aptitude and installs using aptitude, they will be available.
		pkgs='xresprobe laptop-detect discover1'
		numpkgs="$(set -- $pkgs; echo $#)"
		curpkg=0
		db_progress INFO base-config/progress/prep
		for pkg in xresprobe laptop-detect discover1; do
			min="$((40 * $curpkg / $numpkgs))"
			curpkg="$(($curpkg + 1))"
			max="$((40 * $curpkg / $numpkgs))"
			if apt-cache show "$pkg" >/dev/null 2>&1 && \
			   ! dpkg --get-selections | grep "$pkg" | grep -q install; then
				if progress "$min" "$max" apt-get $APT_OPTS -y -f install "$pkg"; then
					extra="$pkg $extra"
				fi
			else
				db_progress SET "$max"
			fi
		done
	else
		db_progress SET 40
	fi

	# Install popularity-contest if not already installed to ask
	# if the user wants to participate.  If the user says no, remove
	# it again.
	# It lets the user choose whether or not to enable it. We need
	# more people using this so we can hope to get better data
	# about who is using what packages in debian.
	# Using apt-get and dpkg --purge to make sure it is not
	# registered as a wanted package by aptitude.
	if ! dpkg --get-selections |
	   egrep -q 'popularity-contest[[:space:]]+install'; then
		db_progress INFO base-config/progress/prep
		# Ignore errors from apt-get, and only remove if it
		# was installed but not enabled
		if progress 40 50 apt-get $APT_OPTS -y -f install popularity-contest ; then
			if grep -q '^PARTICIPATE=\"*yes\"*' /etc/popularity-contest.conf ; then
				dpkg --purge popularity-contest
			fi
		fi
	else
		db_progress SET 50
	fi

	if which aptitude >/dev/null 2>&1 && [ "$NEW" ]; then
		db_get base-config/package-selection
		PATTERN="$RET"
		if [ "$PATTERN" ]; then
			db_progress INFO base-config/progress/prep
			if ! DEBIAN_PRIORITY=high progress 50 850 \
				aptitude $APT_OPTS --without-recommends -y \
					install "$PATTERN"; then
				cleanup
				failure
				db_progress STOP
				# Don't hang if daemons started from
				# maintainer scripts still have debconf fds
				# open.
				db_stop
				exit 0
			fi
		else
			db_progress SET 850
		fi
	else
		db_progress SET 850
	fi

	# Move final sources.list into place, if necessary, so that language
	# packs that aren't on the CD can be installed.
	if [ -f /etc/apt/sources.list.apt-setup ]; then
		db_progress INFO base-config/progress/prep
		mv -f /etc/apt/sources.list.apt-setup /etc/apt/sources.list
		apt-get -o Acquire::gpgv::Options::=--ignore-time-conflict update || true
	fi

	if [ "$KEEP_BASE_DEBS" != yes ]; then
		apt-get -f clean || true
	fi
	
	# Move cached packages that aren't installed by default into the apt
	# cache, so that installing them doesn't require the CD.
	if [ -d /var/cache/archive-copier/ship ]; then
		find /var/cache/archive-copier/ship -type f -print0 | \
			xargs -0r mv -f \
				--target-directory /var/cache/apt/archives
		rmdir --ignore-fail-on-non-empty /var/cache/archive-copier/ship
		rmdir --ignore-fail-on-non-empty /var/cache/archive-copier
	fi

	# Install language packs and language support as required.
	db_get base-config/language-packs
	if [ "$RET" ]; then
		LANGPACKS="$(echo "$RET" | sed 's/,//g')"
	else
		db_get localechooser/supported-locales
		if [ "$RET" ]; then
			LANGPACKS="$(for loc in $(echo "$RET" | sed 's/,//g'); do
				echo "${loc%%_*}"
			done | sort -u)"
		else
			db_get debian-installer/locale
			LANGPACKS="${RET%%_*}"
		fi
	fi
	db_get base-config/install-language-support
	LANGSUPPORT="$RET"
	if [ "$LANGPACKS" ]; then
		db_progress INFO base-config/progress/prep
		db_get base-config/language-pack-patterns
		LPPATTERNS="$RET"
		numpacks="$(set -- $LANGPACKS; echo $#)"
		numpatterns="$(set -- $LPPATTERNS; echo $#)"
		NUMLANGS="$(($numpacks * $numpatterns))"
		if [ "$LANGSUPPORT" = true ]; then
			numsupport="$(set -- $LANGSUPPORT; echo $#)"
			NUMLANGS="$(($NUMLANGS + $numsupport))"
		fi
		CURLANG=0
	fi
	for lp in $LANGPACKS; do
		for pattern in $LPPATTERNS; do
			min="$((850 + 100 * $CURLANG / $NUMLANGS))"
			CURLANG="$(($CURLANG + 1))"
			max="$((850 + 100 * $CURLANG / $NUMLANGS))"
			pack="$(echo "$pattern" | sed "s/\\\$LL/$lp/g")"
			DEBIAN_PRIORITY=high progress "$min" "$max" \
				aptitude $APT_OPTS --without-recommends -y \
				install "$pack" || true
		done
		if [ "$LANGSUPPORT" = true ]; then
			min="$((850 + 100 * $CURLANG / $NUMLANGS))"
			CURLANG="$(($CURLANG + 1))"
			max="$((850 + 100 * $CURLANG / $NUMLANGS))"
			DEBIAN_PRIORITY=high progress "$min" "$max" \
				aptitude $APT_OPTS --without-recommends -y \
				install "language-support-$lp" || true
		fi
	done
	if [ -z "$LANGPACKS" ]; then
		db_progress SET 950
	fi

	# If X was not installed, remove the hardware detection
	# programs. Of course, this fails if the user manually choose
	# to install these, or wants them installed for some other reason.
	# But I cannot help that.
	if ! dpkg --get-selections | grep 'xserver-\(xfree86\|xorg\)' | grep -q install; then
		if [ -n "$extra" ] ; then
			passthrough dpkg --purge $extra || true
		fi
	fi

	cleanup

	# Mark any pending update notifications as seen.
	if [ -d /var/lib/update-notifier/user.d ]; then
		mkdir -p /etc/update-notifier
		find /var/lib/update-notifier/user.d/ -type f -printf '%P\n' \
			| sed "s/\$/ $(date +%s) 0/" \
			> /etc/update-notifier/hooks_seen
	fi

	if type scrollkeeper-update >/dev/null 2>&1; then
		db_progress INFO base-config/progress/documentation-info
		scrollkeeper-update -q || true
	fi
	db_progress SET 1000

	db_progress STOP

	# Don't hang if daemons started from maintainer scripts still have
	# debconf fds open.
	db_stop
	;;
esac
