#!/bin/sh

#
# err exitval message
#	Display message to stderr and exit with exitval.
#
err()
{
	exitval=$1
	shift

	$echo 1>&2 "$0: ERROR: $*"
	exit $exitval
}

#
# warn message
#	Display message to stderr.
#
warn()
{
	$echo 1>&2 "$0: WARNING: $*"
}

#
# log message
#	Display informational message to stdout.
#
log()
{
	[ ! -z "$opt_verbose" ] && $echo "$0: LOG: $*"
}

#
# find_tools
#	Find absolute name for all required tools.
#
find_tools()
{
	for _tool in $1; do
		if [ "X${_tool}" = "Xecho" ]; then
			if [ -x /usr/ucb/echo ]; then
				eval ${_tool}=/usr/ucb/${_tool}
				continue;
			fi
		fi
		for _dir in /bin /sbin /usr/bin /usr/sbin /usr/pkg/bin; do
			if [ -x ${_dir}/${_tool} ]; then
				eval ${_tool}=${_dir}/${_tool}
				break;
			fi
		done

		eval '_tvalue=$'${_tool}
		[ -z "$_tvalue" ] && err 255 "'$_tool' is required but not found."
	done
}

#
# do_prepare <setsdir> <sets> <kerndir> <kern> <archdir>
#	Create architecture-specific layout in <archdir> folder,
#	that is, unpack all required binary sets and kernel images,
#	prepare directory layout etc.
#
do_prepare()
{
	_setsdir="$1"; _sets="$2"; _kerndir="$3"; _kern="$4"; _dir="$5"

	log " + Removing old bits in '$_dir'..."
	$rm -rf $_dir || err 255 "Cannot remove '$_dir' directory."
	$mkdir -p $_dir || err 255 "Cannote create '$_dir' directory."

	log " + Unpacking binary sets:"
	for _set in $_sets; do
		[ ! -f "$_setsdir/$_set" ] && err 255 "Set '$_set' not found."
		log "  . $_setsdir/$_set"
		$gzip -cd "$_setsdir/$_set" | ( cd $_dir && $tar xpf - )
	done

	# Unpack installation kernel image
	[ ! -f "$_kerndir/$_kern" ] &&
		err 255 "Kernel image '$_kerndir/$_kern' not found."

	log " + Unpacking kernel image: $_kerndir/$_kern"
	$cp "$_kerndir/$_kern" $_dir/netbsd.gz
	$gzip -d $_dir/netbsd.gz
	$chmod 755 $_dir/netbsd

	log " + Fix directory layout"
	_fixdirs="altroot home root var tmp"
	cd $_dir && $mv var altvar &&
		for _fixdir in $_fixdirs; do
			[ ! -d $_fixdir ] && $mkdir $_fixdir
		done
}

#
# prepare_nfsroot <client> <cddir>
#	Create NFS boot layout.
#
prepare_nfsroot()
{
	_client="$1" ; _nfsdir="$2/nfsroot"
	_kernels="vmlinux-nfsroot.gz vmlinux.gz vmlinux_RAQ.gz vmlinux_raq-2800.gz"

	_sets="base.tgz etc.tgz"
	_kern="netbsd-INSTALL.gz"

	_setsdir="$_client/cobalt/binary/sets"
	_kerndir="$_client/cobalt/binary/kernel"

	log "Running netboot directory setup..."
	do_prepare "$_setsdir" "$_sets" "$_kerndir" "$_kern" "$_nfsdir"

	log "Copying release binaries onto the disk..."
	( cd $_client && $tar cpf - cobalt ) |
		( cd "$_nfsdir" && tar xpf - )

	log "Copying machine dependent bits..."
	( cd $basedir/data/cobalt && tar cpf - * ) |
		( cd "$_nfsdir" && tar xpf - )

	log "Fixing up bootstrap code..."
	cd "$_nfsdir/dev" && $mknod lcd c 34 0
	cd "$_nfsdir" && ( $mkdir ./boot && $cp netbsd ./boot/ &&
				$gzip -9 ./boot/netbsd )

	cd "$_nfsdir/boot" && 
		for _kernel in $_kernels; do
			$ln -s netbsd.gz $_kernel
		done

	cd "$_nfsdir" && gzip -c9 usr/mdec/boot > cobalt/binary/kernel/boot.gz

	cd "$_nfsdir" && $cp $basedir/data/common/.cshrc root/

	log "Making /dev entries for init(8)..."
	cd $_nfsdir/dev && $sh ./MAKEDEV init panel
}

#
# build_boot_kernel <version> <source> <tooldir> <builddir>
#	Build boot server kernel.
#
build_boot_kernel()
{
	_version="$1"; _source="$2"; _tooldir="$3"; _builddir="$4"

	[ -f "$_builddir/netbsd" ] && {
		log " + Kernel already exists, kernel build skipped"
		return
	}
		
	_nbconfig="$_tooldir/bin/nbconfig"
	_nbmake="$_tooldir/bin/nbmake-i386"
	[ ! -x "$_nbconfig" -o ! -x "$_nbmake" ] &&
		err 255 "There is no 'nbmake' or 'nbconfig' in tool directory."

	_kernel_in="$basedir/data/conf/i386/GENERIC.$_root_version"
	_kernel_out="$_builddir/GENERIC.CDROOT"

	[ ! -f "$_kernel_in" ] &&
		err 255 "Missing kernel config file '$_kernel_in'."

	$rm -rf "$_builddir"
	$mkdir -p "$_builddir" ||
		err 255 "Can't create '$_builddir' directory."

	log " + Making kernel config file..."
	$sed '/^config/d;/SCSI/d' "$_kernel_in" > "$_kernel_out"
	$cat "$basedir/data/conf/i386/kern.no-conf" >> "$_kernel_out"

	log " + Running 'configure' utility..."
	"$_nbconfig" -s "$_source/sys" -b "$_builddir" "$_kernel_out"

	log " + Calculating source dependencies..."
	cd "$_builddir" && "$_nbmake" -j4 depend

	log " + Building the kernel..."
	cd "$_builddir" && "$_nbmake" -j4
}

#
# prepare_cdroot <server> <cddir>
#	Create infrastructure necessary for the host computer to boot.
#
prepare_cdroot()
{
	_server="$1" ; _rootdir="$2"

	_sets="base.tgz etc.tgz"
	_kern="netbsd-GENERIC.gz"

	_setsdir="$_server/i386/binary/sets"
	_kerndir="$_server/i386/binary/kernel"

	log "Creating boot host layout..."
	do_prepare "$_setsdir" "$_sets" "$_kerndir" "$_kern" "$_rootdir"

	log "Copying i386 boot block..."
	_bootfsdir=i386/installation/floppy
	_bootfs="$_server/$_bootfsdir/boot-big.fs"
	[ ! -f "$_bootfs" ] && err 255 "i386 boot block not found."

	_bootfsdir="$_rootdir/$_bootfsdir"
	$mkdir -p $_bootfsdir ||
		err 255 "Cannot create '$_bootfsdir' directory."
	$cp $_bootfs $_bootfsdir

	log "Copying machine dependent bits..."
	( cd $basedir/data/i386 && tar cpf - * ) | ( cd $_rootdir && tar xpf - )

	cd $_rootdir && $cp $basedir/data/common/.cshrc root/

	log "Making /dev entries for init(8)..."
	cd $_rootdir/dev && $sh ./MAKEDEV init
}

#
# build_boot_fs <server> <source> <tooldir> <cddir> <kerndir> <bootfsdir>
#	Unpack released boot-big.fs floppy image and replace default
#	kernel with customized one.
#
build_boot_fs()
{
	_server="$1"; _source="$2"; _tooldir="$3"; _cddir="$4"; _kerndir="$5"
	_bootfsdir="$6"

	$rm -rf "$_bootfsdir"
	$mkdir -p "$_bootfsdir" ||
		err 255 "Can't create '$_bootfsdir' directory."

	_bootfs="$_server/i386/installation/floppy/boot-big.fs"
	[ ! -f "$_bootfs" ] &&
		err 255 "Boot floppy not found, expected '$_bootfs'."

	log " + Unpacking release floppy image..."
	_bootfs_out="$_bootfsdir/boot-big.fs.tar"
	$dd if="$_bootfs" of="$_bootfs_out" bs=8k skip=1
	cd "$_bootfsdir" && $tar vxpf "$_bootfs_out"

	log " + Replacing boot kernel..."
	$gzip -c9 "$_kerndir/netbsd" > "$_bootfsdir/netbsd"

	log " + Building new floppy image..."
	pax="$_tooldir/bin/nbpax"
	nbinstallboot="$_tooldir/bin/nbinstallboot"
	[ ! -x "$pax" -o ! -x "$nbinstallboot" ] &&
		err 255 "No '$pax' or '$nbinstallboot' in tool directory."

	_bootxx_ustarfs="$_cddir/usr/mdec/bootxx_ustarfs"
	[ ! -f "$_bootxx_ustarfs" ] && err 255 "No '$_bootxx_ustarfs' found."

	_buildfloppies_sh="$_source/distrib/common/buildfloppies.sh"
	[ ! -f "$_buildfloppies_sh" ] &&
		err 255 "'$_buildfloppies_sh' script not found."

	(
		cd "$_bootfsdir"

		# The following taken out of i386 release build log.
		# 5760 figure below is the number of 512 byte blocks in
		# 2880 floppy; 13200 is octal representation of 5760.
		PAX="$pax" $sh "$_buildfloppies_sh" \
			-i "$nbinstallboot  -mi386 @IMAGE@ $_bootxx_ustarfs" \
			-p  -m 1 -s .fs cdboot-big 5760 boot \
			USTAR.volsize.13200 netbsd
	)

	log " + Copying out floppy image..."
	_bootfs_out="$_bootfsdir/cdboot-big1.fs"
	[ ! -f "$_bootfs_out" ] &&
		err 255 "'$_bootfs_out' not found, floppy build failed."

	$cp "$_bootfs_out" "$_cddir/i386/installation/floppy/cdboot-big.fs" ||
		err 255 "Can't copy floppy image."
}

#
# fix_boot_fs <server> <source> <cddir> <tooldir> <tmpdir>
#	Rebuild default boot floppy image, uses _root_version
#	defined by prepare_cdinstructions.
#
fix_boot_fs()
{
	_server="$1"; _source="$2"; _cddir="$3"; _tooldir="$4"; _tmpdir="$5"

	log "Building boot floppy..."
	build_boot_kernel "$_root_version" "$_source" "$_tooldir" \
		"$_tmpdir/kern"
	build_boot_fs "$_server" "$_source" "$_tooldir" "$_cddir"  \
		"$_tmpdir/kern" "$_tmpdir/cdboot-big.fs"
}

#
# prepare_cddir <server> <client> <cddir> <filename> <source> <tooldir>
#               <tmpdir>
#	Prepares CD directory suitable for later use with mkisofs(8).
#
prepare_cddir()
{
	_server="$1"; _client="$2"; _cddir="$3"; _filename="$4"; _source="$5"
	_tooldir="$6"; _tmpdir="$7"

	prepare_cdroot "$_server" "$_cddir"
	prepare_nfsroot "$_client" "$_cddir"

	prepare_cdinstructions "$_server" "$_client" "$_cddir"

	# This one uses _nfsroot_version defined by the above function.
	fix_boot_fs "$_server" "$_source" "$_cddir" "$_tooldir" "$_tmpdir"
}

#
# get_version <setsdir> <rootdir>
#	Get NetBSD version.
#
get_version()
{
	_set="$1/comp.tgz" ; _root="$2"

	[ ! -f "$_set" ] && err 255 "'$1' not found, can't extract sys/param.h."

	$gzip -cd "$_set" |
	( cd $_root && $tar vxpf - ./usr/include/sys/param.h ) > /dev/null 2>&1

	_param="$_root/usr/include/sys/param.h"
	[ ! -f "$_param" ] &&
		err 255 "'$_param' is missing, can't get OS version."

	_version=`${awk} '
	/^#define[ 	]*__NetBSD_Version__/ {
		major = ($3 / 100000000);
		minor = ($3 / 1000000) % 100;
		patch = ($3 / 100) % 100;
		printf("%d.%d.%d\n", major, minor, patch);
	}' "$_param"`
}

#
#
# prepare_cdinstructions <server> <client> <rootdir>
#	Fix NetBSD version information in already installed
#	<cdroot>/root/instructions.txt file.
prepare_cdinstructions()
{
	_server="$1" ; _client="$2" ; _rootdir="$3"

	log "Fixing instructions.txt..."

	get_version "$_server/i386/binary/sets" "$_rootdir"
	_root_version=$_version

	get_version "$_client/cobalt/binary/sets" "$_rootdir/nfsroot"
	_nfsroot_version=$_version

	(
		cd "$_rootdir"/root
		$sed "s/__REL__/$_root_version/; s/__HREL__/$_nfsroot_version/"\
			instructions.txt > instructions.txt.1
		$mv instructions.txt.1 instructions.txt
	)
}

#
# create_cd <server> <client> <cddir> <filename> <source> <tooldir>
#           <tmpdir>
#	Store all required binaries in <tmpdir> and build ISO image.
#
create_cd()
{
	prepare_cddir "$@"

	_server="$1"; _client="$2"; _cddir="$3"; _filename="$4"; _source="$5"
	_tooldir="$6"; _tmpdir="$7"

	[ -z "$_filename" ] && {
		warn "No CD image name supplied, suppress image build."
		exit 0
	}
	[ -z "$mkisofs" ] && err 255 "mkisofs(8) not found."

	[ -f "$_filename" ] && $rm -f "$opt_filename"
	log "Making ISO image '$_filename'..."

	_imagedir=`$dirname $_filename`
	[ ! -z "$_imagedir" ] && {
		$mkdir -p $_imagedir ||
			err 255 "Cannot create '$_imagedir' directory."
	}

	_bootfs=i386/installation/floppy/cdboot-big.fs
	$mkisofs -o "$_filename" -b $_bootfs -c boot.catalog -l -J -r \
		-allow-leading-dots "$_cddir"
}

#
# usage
#	Print help screen and quit.
#
usage()
{
	$echo "Usage:"
	$echo "`$basename $0` <parameter>=<value> [<parameter>=<value> ...]"
	$echo ""
	$echo "Obligatory parameters:"
	$echo "    server       Directory containing release(7) layout"
	$echo "                 for boot server"
	$echo "    client       Directory containing release(7) layout"
	$echo "                 for client to netboot"
	$echo "    source       NetBSD sources which were used to build all"
	$echo "                 of the above"
	$echo "    tooldir      Build tools for boot server architecture"
	$echo ""
	$echo "Optional parameters:"
	$echo "    tmpdir       Place to keep CD data"
	$echo "    filename     CD image name"
	$echo ""
	exit 1
}

#
# get_opts
#	Analyse script parameters.
#
get_opts()
{
	# Defaults
	opt_verbose=

	# Command line
	while [ $# -gt 0 ]; do
		case $1 in
		*=*)
			eval "opt_$1"; shift ;;
		-v)
			opt_verbose=1; shift ;;
		-h|-help|--help)
			usage ;;
		*)
			shift ;;
		esac
	done
}

#
# bootstrap
#	Sets up script's execution environment.
#
bootstrap()
{
	find_tools "rm cp mv ln tar mkdir echo mkisofs dirname pwd basename"
	find_tools "gzip mknod chmod awk sed dd cat sh"

	# Define basedir very early to make it available to the rest of the
	# script
	_dirname=`$dirname $0`
	basedir=`cd $_dirname && $pwd`

	get_opts "$@"

	[ -z "$opt_server" ] && usage
	[ -z "$opt_client" ] && usage
	[ -z "$opt_tooldir" ] && usage
	[ -z "$opt_source" ] && usage

	[ -z "$opt_tmpdir" ] && opt_tmpdir="$basedir/cd.tmp"
	[ -z "$opt_indir" ] && opt_indir="$opt_tmpdir/restorecd"
	[ -z "$opt_outdir" ] && opt_outdir="$opt_tmpdir"

	[ -z "$opt_filename" ] && opt_filename="$opt_outdir/restorecd.iso"
}

main()
{
	bootstrap "$@"
	create_cd "$opt_server" "$opt_client" "$opt_indir" "$opt_filename" \
		  "$opt_source" "$opt_tooldir" "$opt_tmpdir"

	exit 0
}

main "$@"
