#!/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

	# XXX hack to avoid permission problem
	[ -d $_dir/var/spool/ftp/hidden ] &&
		rmdir $_dir/var/spool/ftp/hidden

	# /etc/motd will be prepared on union mfs at boot
	$mv $_dir/etc/motd $_dir/etc/motd.tmpl

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

	log " + Unpacking kernel image: $_kerndir/$_kern"
	$gzip -dc "$_kerndir/$_kern" > $_dir/netbsd
	$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-GENERIC.gz"
	_kern_sysinst="netbsd-RAMDISK.gz"

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

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

	log " + Unpacking sysinst kernel: $_kerndir/$_kern_sysinst"
	$gzip -dc "$_kerndir/$_kern_sysinst" > $_dir/netbsd.sysinst
	$chmod 755 $_dir/netbsd.sysinst

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

	[ -d "$_client/shared/mipsel" ] && {
		( cd $_client && $tar cpf - shared/mipsel ) |
			( cd "$_nfsdir" && tar xpf - )
	}

	[ -d "$_client/shared/ALL" ] && {
		( cd $_client && $tar cpf - shared/ALL ) |
			( 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" && ( $mkdir ./boot &&
				$gzip -9c ./usr/mdec/boot > ./boot/boot.gz)

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

#
# 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"

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

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

	$cp $_cddir/usr/mdec/boot $_cddir || err 255 "Can't copy bootloader."
}

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

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

	# This one uses _nfsroot_version defined by the above function.
	prepare_cdinstructions "$_server" "$_client" "$_cddir"
}

#
# 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__/ { printf $6 }' "$_param"`
	$rm -f $_param
}

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

	log "Fixing instructions.txt and boot.cfg..."

	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
	)
	(
		cd "$_rootdir"
		$sed "s/__REL__/$_root_version/; s/__HREL__/$_nfsroot_version/"\
			boot.cfg > boot.cfg.1
		$mv boot.cfg.1 boot.cfg
	)
}

#
# create_cd <server> <client> <cddir> <filename> <source> <makefs>
#           <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"
	_makefs="$6"; _tmpdir="$7"

	[ -z "$_filename" ] && {
		warn "No CD image name supplied, suppress image build."
		exit 0
	}

	[ -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."
	}

	_specfile=$_tmpdir/spec
	_specroot=$_tmpdir/spec.root
	_specnfsroot=$_tmpdir/spec.nfsroot
	$rm -f $_specfile $_specroot $_specnfsroot
	# /etc/motd will be generated from /etc/motd.tmpl on union mfs
	# /var is mounted as mfs and files will be copied from /altvar
	$cat $_cddir/etc/mtree/set.base \
	     $_cddir/etc/mtree/set.etc | \
	     $sed -e 's,^\./etc/motd,\./etc/motd.tmpl,' \
	          -e 's,^\./var,./altvar,' \
	          -e 's/ size=[0-9]*//' > $_specroot
	$cat $_cddir/nfsroot/etc/mtree/set.base \
	     $_cddir/nfsroot/etc/mtree/set.etc | \
	     $sed -e 's,^\./etc/motd,\./etc/motd.tmpl,' \
	          -e 's,^\./var,./altvar,' \
	          -e 's,^\./,./nfsroot/,' \
	          -e 's/ size=[0-9]*//' > $_specnfsroot
	$cat $_cddir/etc/mtree/set.base | \
	$cat $_specroot $basedir/data/common/spec.root \
	    $_specnfsroot $basedir/data/common/spec.nfsroot > $_specfile

	_bootxx=$_cddir/usr/mdec/bootxx_cd9660
	"$_makefs" -t cd9660 -F $_specfile -N $_cddir/etc -o \
	    rockridge,label=restorecd,bootimage=i386\;${_bootxx},no-emul-boot,allow-multidot \
	    "$_filename".tmp $_cddir && \
	    mv "$_filename".tmp "$_filename"
}

#
# 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 "    makefs       Path to makefs(1) tool"
	$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 dirname pwd basename"
	find_tools "gzip 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_source" ] && usage
	[ -z "$opt_makefs" ] && 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_makefs" "$opt_tmpdir"

	exit 0
}

main "$@"
