#!/bin/sh -ef
#
# Copyright (C) 2003-2008  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2007  Alex V. Myltsev <avm@altlinux.org>
# 
# The chroot cache functions for the hsh-initroot
#
# This file 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.
#

rebuild_chroot_cache=

archive_chroot_cache()
{
	[ -n "$rebuild_chroot_cache" ] || return 0

	local rpmdb_archive="$cache_dir/chroot/rpmdb.tar"
	local chroot_archive="$cache_dir/chroot/chroot.cpio.lzo"

	trap 'rm -f -- "$rpmdb_archive" "$chroot_archive"' EXIT HUP INT QUIT TERM

	# rpmdb
	tar -cf "$rpmdb_archive" -C aptbox/var/lib/rpm/ . &&
		verbose 'RPM database archivation complete.' ||
		fatal 'RPM database archivation failed.'

	# chroot
	create_entry_fakeroot_header
	cat >>"$entry" <<__EOF__
cd /
find --version >/dev/null 2>&1 && find=find || find=/.host/find
cpio --version >/dev/null 2>&1 && cpio=cpio || cpio=/.host/cpio
\$find * ! -path dev ! -path dev/\\* |\$cpio --create --quiet --format=newc
__EOF__

	rm -f "$cache_dir/chroot/err"
	{ wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long \
	    chrootuid1 </dev/null || echo 1 >"$cache_dir/chroot/err"; } |
		{ lzop >"$chroot_archive" || echo 1 >"$cache_dir/chroot/err"; }
	if [ -f "$cache_dir/chroot/err" ]; then
		rm -f "$cache_dir/chroot/err"
		fatal 'Chroot archivation failed.'
	fi
	verbose 'Chroot archivation complete.'

	trap - EXIT HUP INT QUIT TERM

	# Regenerate data for later cache validation.
	mv -f $verbose "$cache_dir"/chroot/list{.new,}/init >&2
	mv -f $verbose "$cache_dir"/chroot/list{.new,}/build >&2
	rm -rf "$cache_dir"/chroot/package
	mv -f $verbose "$cache_dir"/chroot/package{.new,} >&2
	mv -f $verbose "$cache_dir"/chroot/fakeroot-options{.new,} >&2
} # archive_chroot_cache

check_chroot_cache()
{
	[ -z "$no_cache" ] || return 0

	local f n
	local bad="$cache_dir/chroot/list.new/bad"
	rm -f "$bad"
	cat "$cache_dir"/chroot/list.new/{init,build} |
		while read f; do
			[ -n "$f" ] || continue
			n="${f##*/}"
			if ! touch -r "$f" "$cache_dir/chroot/package.new/$n"; then
				# Don't break early, show the whole list of missing RPMs.
				printf %s\\n "$n" >>"$bad"
				#break
			fi
		done
	[ ! -f "$bad" ] || fatal 'Failed to check chroot cache.'

	printf %s "$save_fakeroot" >"$cache_dir/chroot/fakeroot-options.new"

	while :; do
		for f in "$cache_dir"/chroot/{rpmdb.tar,chroot.cpio.lzo}; do
			if [ ! -s "$f" ]; then
				verbose "Missing $f archive, invalidating chroot cache."
				rebuild_chroot_cache=1
				break 2
			fi
		done

		if ! cmp -s "$cache_dir"/chroot/fakeroot-options{.new,}; then
			verbose 'Fakeroot options changed, invalidating chroot cache.'
			rebuild_chroot_cache=1
			break
		fi

		if ! cmp -s "$cache_dir"/chroot/list{.new,}/init; then
			verbose 'Init list changed, invalidating chroot cache.'
			rebuild_chroot_cache=1
			break
		fi

		if ! cmp -s "$cache_dir"/chroot/list{.new,}/build; then
			verbose 'Build list changed, invalidating chroot cache.'
			rebuild_chroot_cache=1
			break
		fi

		rm -f "$bad"
		cat "$cache_dir"/chroot/list.new/{init,build} |
			while read f; do
				n="${f##*/}"
				if [ "$cache_dir/chroot/package.new/$n" -nt "$cache_dir/chroot/package/$n" -o \
				     "$cache_dir/chroot/package.new/$n" -ot "$cache_dir/chroot/package/$n" ]; then
					verbose "package $n changed, invalidating chroot cache"
					printf %s\\n "$n" >"$bad"
					break
				fi
			done
		if [ -f "$bad" ]; then
			rebuild_chroot_cache=1
			break
		fi
		break
	done
} # check_chroot_cache

unpack_chroot_cache()
{
	local f

	# rpmdb
	f="$cache_dir/chroot/rpmdb.tar"
	find aptbox/var/lib/rpm/ -mindepth 1 -depth -delete
	tar -xf "$f" -C aptbox/var/lib/rpm/ &&
		verbose "Unpacked $f." ||
		fatal "Unpack of $f failed."

	# chroot
	f="$cache_dir/chroot/chroot.cpio.lzo"
	lzop -dc <"$f" |
	wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long \
	    chrootuid1 /.host/cpio --extract --make-directories --sparse --quiet --preserve-modification-time &&
		verbose "Unpacked $f." ||
		fatal "Unpack of $f failed."
	if [ -n "$save_fakeroot" ]; then
		wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short \
		    chrootuid1 /bin/touch /.fakedata </dev/null &&
			verbose "Created fakedata." ||
			fatal "Failed to create fakedata."
		create_entry_fakeroot_header
		cat >>"$entry" <<__EOF__
cd /
cpio --extract --make-directories --sparse --quiet --unconditional --preserve-modification-time
__EOF__
		lzop -dc <"$f" |
		wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long \
		    chrootuid1 &&
			verbose "Unpacked $f for fakedata." ||
			fatal "Unpack of $f for fakedata failed."
	fi
	create_entry_fakeroot_header
	cat >>"$entry" <<__EOF__
if grep -qs ^rooter: /etc/passwd 2>/dev/null; then
	userdel rooter
	groupadd -g $gid1 rooter
	useradd -M -u $uid1 -g $gid1 -d /root rooter
fi
if grep -qs ^builder: /etc/passwd 2>/dev/null; then
	userdel builder
	groupadd -g $gid2 builder
	useradd -M -u $uid2 -g $gid2 -d /usr/src builder
	chmod $verbose 1777 /usr/src >&2
fi
__EOF__

	wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short wlimit_bytes_written=$wlimit_bytes_out \
	    chrootuid1 </dev/null &&
		verbose 'First time initialization complete.' ||
		fatal 'First time initialization failed.'
} # unpack_chroot_cache

create_chroot()
{
	copy_chroot_incoming $initlist

	local f
	for f in $initlist; do
		rpm2cpio "$f" |wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long \
		    chrootuid1 /.host/cpio --extract --make-directories --sparse --quiet --preserve-modification-time &&
			verbose "Unpacked ${f##*/}." ||
			fatal "Unpack of ${f##*/} failed."
	done
	verbose 'Unpacked initial package list.'
	if [ -n "$save_fakeroot" ]; then
		wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short \
		    chrootuid1 /bin/touch /.fakedata </dev/null &&
			verbose "Created fakedata." ||
			fatal "Failed to create fakedata."
		create_entry_fakeroot_header
		cat >>"$entry" <<__EOF__
cd /
cpio --extract --make-directories --sparse --quiet --unconditional --preserve-modification-time
__EOF__
		for f in $initlist; do
			rpm2cpio "$f" |wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long \
			    chrootuid1 &&
				verbose "Unpacked ${f##*/} for fakedata." ||
				fatal "Unpack of ${f##*/} for fakedata failed."
		done
	fi

	create_entry_fakeroot_header
	cat >>"$entry" <<__EOF__
# WORKAROUND: mtab is missing in setup-2.2.2-alt1
touch /etc/mtab
# /WORKAROUND
# WORKAROUND: filesystem < 2.1.7-alt1 doesn't provide /sys
mkdir -p /sys
# /WORKAROUND
rm -f /etc/rpm/macros.db1
# WORKAROUND: glibc-locales is too large
echo '%_install_langs ${install_langs:-$def_install_langs}' >>/etc/rpm/macros
# /WORKAROUND
rpmdb --initdb
__EOF__
	verbose "Created entry point: $entry"

	wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short wlimit_bytes_written=$wlimit_bytes_out \
	    chrootuid1 </dev/null &&
		verbose 'Created RPM database.' ||
		fatal 'Failed to create RPM database.'

	create_entry_fakeroot_header
	cat >>"$entry" <<__EOF__
export DURING_INSTALL=1
${exclude_docs:+export RPM_EXCLUDEDOCS=1}
${rpmi:-$def_rpmi} -i $verbose --ignorearch $exclude_docs --justdb --nodeps $(for f in $initlist; do printf %s "\"$(quote_shell "${f##*/}")\" "; done)
__EOF__

	wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_short \
	    chrootuid1 </dev/null &&
		verbose 'Installed initial package list.' ||
		fatal 'Failed to install initial package list.'

	purge_chroot_in
	update_RPM_database --nodeps $initlist

	for f in /etc/host.conf /etc/hosts /etc/resolv.conf; do
		if [ -r "$f" -a -s "$f" ]; then
			create_entry_fakeroot_header
			cat >>"$entry" <<__EOF__
cat >$f
__EOF__
			wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short wlimit_bytes_written=$wlimit_bytes_out \
			    chrootuid1 <"$f" &&
				verbose "Installed $f file." ||
				fatal "Failed to install $f file."
		fi
	done

	# At this stage, RPM inside chroot is fully functional.

	if [ -n "$buildlist" ]; then
		copy_chroot_incoming $buildlist

		create_entry_fakeroot_header
		cat >>"$entry" <<__EOF__
set -f
export DURING_INSTALL=1
${exclude_docs:+export RPM_EXCLUDEDOCS=1}
# Exclude packages which should not be installed
inst_list="$(for f in $buildlist; do
	f="${f##*/}"
	for p in $noinstall_pattern_list; do
		if [ -z "${f##$p}" ]; then
			continue 2
		fi
	done
	printf %s "$(quote_shell "$f") "
done)"
noinst_list="$(for f in $buildlist; do
	f="${f##*/}"
	for p in $noinstall_pattern_list; do
		if [ -z "${f##$p}" ]; then
			printf %s "$(quote_shell "$f") "
			continue 2
		fi
	done
done)"
[ -z "\$noinst_list" ] || rpm_nodeps="--nodeps"
[ -z "\$inst_list" ] || ${rpmi:-$def_rpmi} -i --ignorearch $exclude_docs \$rpm_nodeps \$inst_list
[ -z "\$noinst_list" ] || ${rpmi:-$def_rpmi} -i --ignorearch $exclude_docs --nodeps --justdb \$noinst_list
__EOF__

		wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_short \
		    chrootuid1 </dev/null &&
			verbose 'Installed build package list.' ||
			fatal 'Failed to install build package list.'

		purge_chroot_in
		update_RPM_database $buildlist

		# First time scripts.

		create_entry_fakeroot_header
		cat >>"$entry" <<__EOF__
ldconfig
if type adjust_kernel_headers >/dev/null 2>&1; then
	adjust_kernel_headers
fi
groupadd -g $gid0 caller ||:
useradd -M -u $uid0 -g $gid0 -d / caller ||:
groupadd -g $gid1 rooter
useradd -M -u $uid1 -g $gid1 -d /root rooter
groupadd -g $gid2 builder
useradd -M -u $uid2 -g $gid2 -d /usr/src builder
chmod $verbose 1777 /usr/src >&2
__EOF__

		wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short wlimit_bytes_written=$wlimit_bytes_out \
		    chrootuid1 </dev/null &&
			verbose 'First time initialization complete.' ||
			fatal 'First time initialization failed.'
	fi # $buildlist

	archive_chroot_cache
} # create_chroot
