#!/bin/sh -e

export LC_ALL=C LANG=C LANGUAGE=C
export GZIP_OPTS=${GZIP:=-9}

. spt-sh-functions

number=
mkiso=1
mkboot=1
nobootsplash=
nocleanup=
nocompress=
quiet=
isoname=
profile_dir=
ARCH=
apt_config=
apt_prefix=
excludedocs=
image_type='squashfs'

outdir=
tmpdir=
exit_handler() {
	local rc=$?
	trap - EXIT
	if [ -z "$nocleanup" ]; then
		hsh ${number:+--number="$number"} --cleanup -- "$workdir"
		rm -rf -- "$tmpdir" "$outdir"
	fi
	exit $rc
}

make_boot() {
	rm -rf -- "$outdir/isolinux"
	[ ! -d "$chroot/.isolinux" ] ||
		cp -ra -- "$chroot/.isolinux" "$outdir/isolinux"
}

make_splash() {
	rm -rf -- "$outdir/splash"
	[ ! -f "$chroot/.splash/bootsplash" ] ||
		cp -ra -- "$chroot/.splash" "$outdir/splash"
}

create_image() {
	Verbose "Creating image for \`$IDENT'."

	[ -z "$nocompress" ] || EXTRAOPTS=${EXTRAOPTS:-"-noI -noD -noF"}

	case "$image_type" in
	    squashfs)
		init_chroot_progs "squashfsprogs"
		cp -a "$tmpdir/.archive.tar" "$chroot/tmp/"
		cat >"$tmpdir/mkfs"<<-EOF
		mkdir -p /tmp/.archive
		tar -C /tmp/.archive -xf /tmp/.archive.tar
		mksquashfs /tmp/.archive /.altlinux.img $EXTRAOPTS
		EOF
		chroot_exec "$tmpdir/mkfs"
		;;
	    tbz2)
		mkiso=
		cat "$tmpdir/.archive.tar" | bzip2 -f > "$workdir/$OUT.tar.bz2"
		;;
	    tgz)
		mkiso=
		cat "$tmpdir/.archive.tar" | gzip -f "$GZIP_OPTS" > "$workdir/$OUT.tar.gz"
		;;
	    tar)
		mkiso=
		cp -f "$tmpdir/.archive.tar" "$workdir/$OUT.tar"
		;;
	    *)
		Fatal 'Unsupported image type.'
		;;
	esac
	# move image from chroot to outdir
	[ ! -f "$chroot/.altlinux.img" ] ||
		mv -f -- "$chroot/.altlinux.img" "$outdir/$OUT"
}

make_image() {
	[ -z "$COPYONLY" ] || return 0
	init_chroot_progs

	# Install packages
	local copy_init_list="$(grep -hs '^[^#]' "$profile_dir/$IDENT/packages" "$profile_dir/$IDENT/packages.$CDFILENAME")"
	[ -z "$copy_init_list" ] ||
		hsh-install "$workdir" $verbose $quiet $excludedocs \
			${number:+--number="$number"} \
			$copy_init_list
	[ -z "$FILES" ] ||
		rsync $verbose -rtlpC "$profile_dir/$FILES" "$chroot/.in/" ||
		Fatal "Could not copy additional files for \`$comp' component."

	chmod +r $chroot/.{host,in,out}
	chroot_mkdev

	# Run postinstll hacks
	Verbose "Run hooks for: $IDENT"
	postinstall "$profile_dir/$IDENT"

	[ -z "$mkboot" ] || make_boot

	[ ! -z "$nobootsplash" ] || make_splash

	# make archive for image
	Verbose "Creating archive image for \`$IDENT'."
	cp -at "$chroot/.host" /bin/tar
	cat >"$tmpdir/mktar"<<-EOF
	tar --numeric-owner \
	--exclude /.archive.tar --exclude /.host --exclude /.image --exclude /.iso --exclude /.isolinux \
	--exclude /.splash --exclude /.in --exclude /.out --exclude /.fakedata --exclude /usr/lib*/*fakeroot* \
	-cf /.archive.tar /
	EOF
	chroot_exec "$tmpdir/mktar" &&
	    Verbose 'Chroot image arhivation complete.' ||
	    Fatal 'Chroot image arhivation failed.'

	[ ! -f "$chroot/.archive.tar" ] ||
		cp -at "$tmpdir" "$chroot/.archive.tar"

	[ -n "$NOIMAGE" ] || create_image
}

copyonly() {
	[ -n "$COPYONLY" ] || return 0
	local copy_init_list= copy_filelist packages

	if [ -n "$HOOKPKGS" ]; then
		Verbose "Installation packages for hook"
		hsh-install "$workdir" $verbose $quiet $excludedocs ${number:+--number="$number"} $HOOKPKGS
	fi

	packages="$profile_dir/$IDENT/packages"

	# $packages - file may be empty or not exist
	[ ! -f "$packages" ] ||
		copy_init_list="$(grep -hs '^[^#]' "$profile_dir/$IDENT/packages"  "$profile_dir/$IDENT/packages.$CDFILENAME")" ||:

	copy_filelist="$(print_uris setup filesystem rpm $copy_init_list)" ||
		Fatal 'Failed to generate package file list.'

	[ -n "$copy_filelist" ] ||
		Fatal 'Nothing to copy.'

	mkdir -p -- "$chroot/.in/$INSTALLDIR"
	echo $copy_filelist | xargs   install -p -m644 $verbose -t "$chroot/.in/$INSTALLDIR/" --  ||
		Fatal "Could not copy packages from $IDENT packages to \`/.in'."

	[ -z "$FILES" ] ||
		rsync $verbose -rtlpC "$profile_dir/$FILES" "$chroot/.in/" ||
		Fatal "Could not copy additional files for \`$comp' component."

	chroot_run rsync -rtlpC --delete-before "/.in/" "$imgdir/"
	find "$chroot/.in/" -mindepth 1 -delete

	# Run postinstll hacks
	Verbose "Run hooks for: $IDENT"
	postinstall "$profile_dir/$IDENT"

	# Cleanup after all
	chroot_run rm -rf -- "$imgdir"
}

runto_components() {
    local cmd comp
    local IDENT VENDOR COPYONLY INSTALLDIR NOIMAGE EXTRAOPTS FILES OUT

    cmd="$1" && shift

    for comp in $COMPONENTS; do
	IDENT="packages" VENDOR="ALTLinux" COPYONLY= INSTALLDIR= NOIMAGE= EXTRAOPTS= FILES= OUT=

	. "$profile_dir/$comp/config" ||
		Fatal "File \`$profile_dir/$comp/config' not found."

	if [ ! -f "$profile_dir/$IDENT/packages" ]; then
		Verbose "$profile_dir/$IDENT/packages: not found"
		continue
	fi
	if [ ! -s "$profile_dir/$IDENT/packages" ]; then
		Verbose "$profile_dir/$IDENT/packages: empty"
		continue
	fi

	Verbose "Making component $comp"
	eval "$cmd" "$@"
    done
}

show_help() {
    [ -z "$*" ] || Info "$*"
    cat <<EOF
spt - make image of custom configured system using profile

Usage: $PROG [options] <path-to-workdir>

Valid options are:
  --number=NUMBER          subconfig identifier
  --noiso                  do not make ISO image
  --noboot                 do not make boot actions (like initrd/syslinux/etc)
  --nobootsplash           do not add bootsplash image
  --no-cleanup             do not remove temporary directory
  --no-compress            do not compress squashfs image
  --isoname=NAME           ISO image filename
  --image-type=TYPE        image archive/filesystem type:
     TYPE:
        squashfs           squashfs (default)
        ext2               ext2fs
        tbz2               tarball + bzip2 (always --noiso)
        tgz                tarball + gzip  (always --noiso)
  --excludedocs            exclude docs during install
  --arch=ARCH              target architecture
  --apt-config=FILE        path to custom apt.conf file
  --apt-prefix=DIR         path to apt directory prefix (e.g. /usr)
  -p, --profile-dir=DIR    path to profile dir.
  -q, --quiet              try to be more quiet
  -v, --verbose            print a message from each action
  -V, --version            print program version and exit
  -h, --help               show this text and exit

Report bugs to http://bugs.altlinux.org/

EOF
    exit
}

TEMP=`getopt -n $PROG -o p:,v,q,V,h -l noiso,noboot,nobootsplash,no-cleanup,no-compress,number:,isoname:,image-type:,excludedocs,arch:,apt-config:,apt-prefix:,profile-dir:,verbose,help,quiet,version -- "$@"` || show_usage
eval set -- "$TEMP"

while :; do
    case "$1" in
	--noiso) mkiso=
		;;
	--noboot) 
		mkboot=
		mkiso=
		;;
	--nobootsplash) nobootsplash=1
		;;
	--no-cleanup) nocleanup=1
		;;
	--no-compress) nocompress=1
		;;
	--number) shift
		[ -z "$1" ] || number="$1"
		;;
	--isoname) shift; isoname="${1##*/}"; mkiso=1
	    ;;
	--image-type) shift; 
	    case "$1" in
		    squashfs)
			    image_type="$1"
			    ;;
		    tbz2|tgz|tar)
			    image_type="$1"
			    mkboot=
			    mkiso=
			    ;;
		    *)
			    Fatal "--image-type: $1: not known image type."
			    ;;
	    esac
	    ;;
	--excludedocs) excludedocs=--excludedocs
	    ;;
	--arch) shift; ARCH="$1"
	    [ -x "$ARCH" ] || ARCH=$(uname -m)
	    ;;
	--apt-config) shift
		apt_config="$(readlink -ev "$1")" && 
			[ -r "$apt_config" ] ||
			Fatal "$1: file not available."
	    ;;
	--apt-prefix) shift
		apt_prefix="$(readlink -ev "$1")" &&
			[ -d "$apt_prefix" -a -x "$apt_prefix" ] ||
			Fatal "$1: directory not available."
	    ;;
	-p|--profile-dir) shift; profile_dir="$1"
	    ;;
	-v|--verbose) verbose=-v
	    ;;
	-q|--quiet) quiet=-q
	    ;;
	-h|--help) show_help
	    ;;
	-V|--version) print_version "$PROG"
	    ;;
	--) shift; break
	    ;;
	*) Fatal "Unrecognized option: $1"
	    ;;
    esac
    shift
done

# Exactly one argument, please.
[ "$#" -ge 1 ] || show_help 'Insufficient arguments.'
[ "$#" -le 1 ] || show_usage 'Too many arguments.'

[ -d "$1" ] || mkdir "$1"
workdir="$(readlink -ev "$1")" && shift
profile_dir="${profile_dir:-$workdir/profile}"
[ -f "$profile_dir/config" ] || show_usage 'Profile in workdir not found. Please, init directories stucture first.'
. "$profile_dir/config"

[ -n "$COMPONENTS" ] || Fatal "Please, add COMPONENTS to configuration file"
chroot="$workdir/chroot"
tmpdir="$workdir/tmp"
outdir="$workdir/out"

trap exit_handler HUP PIPE INT QUIT TERM EXIT
remkdir "$tmpdir"
remkdir "$outdir"

runto_components make_image

[ -n "$mkiso" -o -n "$mkboot" ] || exit 0
# install init packages
init_chroot_progs "mar propagator syslinux mkisofs rsync glibc-utils $KERNEL_ADD"

isodir=/.iso
imgdir=/.image

rsync $verbose -rtlpC "$outdir/" "$chroot/$imgdir/"
chroot_run mkdir -p "$isodir/isolinux/alt0"
chroot_run rsync -rtlpC "$imgdir/" "$isodir/"
rm -rf -- "$chroot/$imgdir"

# COPYONLY handle
runto_components copyonly

# syslinux stuff
addisolinux=
if [ -f "$profile_dir/isolinux.cfg.in" ]; then
	cp "$profile_dir/isolinux.cfg.in" "$chroot/.in/isolinux.cfg"
	addisolinux=1
fi
[ ! -f "$profile_dir/syslinux.cfg.in" ] ||
	cp "$profile_dir/syslinux.cfg.in" "$chroot/.in/syslinux.cfg"

# copy profile & utils
cp -af "$profile_dir"/{modules,initfs} "$chroot/.host/"

# copy optional files
[ ! -d "$profile_dir/isofiles" ] ||
	rsync -rtlpC "$profile_dir/isofiles/" "$chroot/.host/isofiles/"

CDFILENAME=${isoname:-$CDFILENAME}
CDFILENAME=${CDFILENAME:-livecd.iso}

cat >"$tmpdir/mkboot"<<EOF
#!/bin/sh
# kernel image
mkdir -p $isodir/isolinux/alt0
find /.in/ -name '*.cfg' -exec cp -at $isodir/isolinux/ -- \{\} \+
cp -a /boot/vmlinuz-* $isodir/isolinux/alt0/vmlinuz
kver=\$(rpm -qa --qf '%{VERSION}-%{NAME}-%{RELEASE}' kernel-image-* | sed -e 's,kernel\-image\-,,')
libdir="\$(getconf LIBDIR)"

# Add native syslinux
if [ -n "$addisolinux" ]; then
	cp "/usr/lib/syslinux/isolinux.bin" "$isodir/isolinux/"
	isolinux-config --base /isolinux "$isodir/isolinux/isolinux.bin"
fi

# copy optional files
[ ! -d "/.host/isofiles" ] ||
	rsync -rtlpC /.host/isofiles/ "$isodir/"

# generate marfile & co
mkdir -p /tmp/mkmar
if [ -x /usr/bin/mkmodpack ]; then
    /usr/bin/mkmodpack -p /.host/modules -o /tmp/mkmar/modules -k "\$kver"
else
    /usr/bin/mkmar -r / -p /.host/modules -o /tmp/mkmar/modules -k "\$kver"
fi

# generate .VERSION
echo "${ORIGIN:+$ORIGIN }${VERSION:+$VERSION }${SUITE:+$SUITE }${CODENAME:+($CODENAME)}" > /tmp/.VERSION

if [ -z "$nobootsplash" -a -f $isodir/splash/bootsplash ]; then
	mv $isodir/splash /bootsplash
fi

if [ -f /tmp/mkmar/modules ]; then
    cat "\$libdir/propagator/initfs" /tmp/mkmar/modules > $isodir/isolinux/alt0/full.cz
    sed -e "/@MODDIR@/d" \
	-e "s|@DOTVERSION@|/tmp/.VERSION|" \
	-e "s|@LIBDIR@|\$libdir|" \
	< /.host/initfs | gencpio - | gzip -c \
	>> $isodir/isolinux/alt0/full.cz
else
    sed -e "s|@MODDIR@|/tmp/mkmar|g" \
	-e "s|@DOTVERSION@|/tmp/.VERSION|" \
	-e "s|@LIBDIR@|\$libdir|" \
	< /.host/initfs | gencpio - | gzip -c \
	> $isodir/isolinux/alt0/full.cz
fi

mkdir -p $isodir/.disk
echo "${PUBLISHER:+$PUBLISHER }${VERSION:+$VERSION }${LABEL:+$LABEL }${CODENAME:+($CODENAME)}" > $isodir/.disk/info

mkisofs -b isolinux/isolinux.bin \
	-c isolinux/boot.cat \
	-copyright "LICENSE.html" \
	--volset "\$(cat $isodir/.disk/info)" \
	-V "$SUITE" \
	-publisher "$PUBLISHER" \
	-sysid "$ORIGIN" \
	-no-emul-boot \
	-boot-info-table \
	-boot-load-size 4 \
	-J -l -r \
	-o "/tmp/$CDFILENAME" "$isodir"
EOF

chroot_exec "$tmpdir/mkboot" &&
   Verbose 'Boot stuff make complete.' ||
   Fatal 'Boot stuff make failed.'

[ ! -f "$chroot/tmp/$CDFILENAME" ] ||
	cp -af "$chroot/tmp/$CDFILENAME" "$workdir/"

chroot_run rm -f "/tmp/$CDFILENAME"
