#!/bin/zsh
#
# dyne:bolic software development kit, third edition.
# also known as dyne:III SDK
#
# The dyne:SDK is
# Copyright (C) 2001-2011 by Denis Roio <jaromil@dyne.org>
#
# This source code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Public License as published 
# by the Free Software Foundation; either version 3 of the License,
# or (at your option) any later version.
#
# This source code 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.
# Please refer to the GNU Public License for more details.
#
# You should have received a copy of the GNU Public License along with
# this source code; if not, write to:
# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

DEBUG=1
BINFILE=`basename $0`
BINPATH=`dirname $0`

autoload colors; colors


# standard output message routines
# it's always useful to wrap them, in case we change behaviour later
# standard output message routines
# it's always useful to wrap them, in case we change behaviour later
notice() { if ! [ $QUIET ]; then print "$fg_bold[green][*]$fg_no_bold[white] $1" >&2; fi }
error()  { if ! [ $QUIET ]; then print "$fg[red][!]$fg[white] $1" >&2; fi }
func()   { if [ $DEBUG ]; then   print "$fg[blue][D]$fg[white] $1" >&2; fi }
act()    {
    if ! [ $QUIET ]; then
	if [ "$1" = "-n" ]; then
	    print -n "$fg_bold[white] . $fg_no_bold[white] $2" >&2;
	else
	    print "$fg_bold[white] . $fg_no_bold[white] $1" >&2;
	fi
    fi
}

PACKAGE="dyne:OS SDK"
VERSION="3.0"
DATE_SUFFIX="`date +%F_%H-%M-%S`"

if [ $DYNESDK ]; then
    SDK=${DYNESDK}
else
    SDK=`pwd`
fi

notice "$PACKAGE version $VERSION   Software Development Kit by \
$fg_bold[black]$bg[green]RAS$bg[yellow]TAS$bg[red]OFT$fg_no_bold[white]$bg[black]"
ARGS=$@[@]

# CHECK FOR BINARIES:
which sudo mksquashfs unsquashfs genisoimage >/dev/null
if [ $? != 0 ]; then
    error "A program required by dyne:SDK is missing: please run dynesdk install"
    exit 1
fi

# sudo, unsquashfs, Xephyr

OPTS=`getopt -o X:o:hv -n "$BINFILE" -- "$@"`

while true; do
    case "$1" in
	-h)
	    notice "synopsis: $BINFILE [options] command [subcommand]"
	    notice "commands:"
	    act "create open a livecd ISO into an new SDK dir"
	    act "enter  chroot into an SDK system (use -X for graphical)"
	    act "pack   squash and bake SDK components into a live ISO"
            notice "options:"
	    act "-x RES nest graphical session at RES (ie: 1024x768)"
	    act "-o     name for the output file or directory"
	    act "-h     print this help"
	    act "-v     print out the version of this tool"

	    echo; exit 2 ;;
	-X) XRES=${2}; shift 2 ;;
	-o) OUTFILE=${2}; shift 2 ;;
	-v) exit 2 ;;
	-p) PFX=${2}; shift 2 ;;
	--) shift; break ;;
	*)  CMD=${1}; ARG1=${2}; ARG2=${3}; break ;;
    esac
done

act "$BINFILE $ARGS"


# setup the name for the output directory
if [ -z $OUTFILE ]; then
    dest="dyneIII"
else
    dest=${OUTFILE}
fi
act "destination set to: $dest"

## INTERNAL FUNCTIONS

# escalate privileges
check_priv() {
    if [ $UID != 0 ]; then
	func "Using sudo for root execution of '$BINFILE ${(f)ARGS}'"
	# check if sudo has a timestamp active
	sudok=false
	sudo -n ${BINPATH}/${BINFILE} 2> /dev/null
	if [ $? != 0 ]; then # if not then ask a password
	    cat <<EOF | pinentry 2>/dev/null | awk '/^D/ { print $2 }' | sudo -S -v
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE Super user privileges required
SETDESC Sudo execution of $BINFILE ${ARGS[@]}
SETPROMPT Insert your USER password:
GETPIN
EOF
	fi
	sudo "${BINPATH}/${BINFILE}" ${(s: :)ARGS}
	exit $?
    fi # are we root already
    return 0
}

# safe dir creation function
safe_dir() {
    which mktemp &> /dev/null
    if [[ $? = 0 ]]; then
        mktemp -d /dev/shm/$1.XXXX.$$
        return
    fi
    dir="/dev/shm/$1.$RANDOM.$RANDOM.$$"
    (umask 077 && mkdir "$dir") || print "-1"
    print "$dir"
}

loop_mount_iso() {

    file $1 | grep 'ISO.*filesystem' > /dev/null
    if [ $? != 0 ]; then
	error "$1 doesn't appear to be an ISO filesystem"
	return 1
    fi
    export ISOLOOP="`safe_dir isoloop`"

    mkdir -p $ISOLOOP

    mount -o loop $1 $ISOLOOP
    if [ $? != 0 ]; then
	error "cannot loop mount iso $dirname/$filename"
	act "trying to unmount..."
	umount $ISOLOOP
	if [ $? != 0 ]; then
	    error "errors reported loop mounting iso, operation aborted."
	    rmdir $ISOLOOP
	    return 1
	else
	    mount -o loop $1 $ISOLOOP
	    if [ $? != 0 ]; then
		error "cannot loop mount iso $dirname/$filename"
		rmdir $ISOLOOP
		return 1
	    fi
	fi
    fi

    act "loop mounted on $ISOLOOP"
    if ! [ -x $ISOLOOP/live ]; then
	error "ISO doesn't contains a live/ directory, is this a live-build system?"
	umount $ISOLOOP
	rmdir $ISOLOOP
	error "operation aborted."
	return 1
    fi

    return 0

}

loop_umount_iso() {
    if ! [ $ISOLOOP ]; then
	error "no ISO seems to be loop mounted at this time"
	error "operation aborted"
	return 1
    fi

    umount $ISOLOOP
    rmdir $ISOLOOP
    return 0
}

########################################################################
### COMMAND FUNCTIONS
########################################################################

create_sdk() {

    if ! [ $ARG1 ]; then
	error "argument missing: ISO file"
	exit 1
    fi

    if ! [ -r $ARG1 ]; then
	error "$ARG1 not found"
	return 1
    fi

    notice "Creating dyne:SDK from $ARG1 into $dest"

    loop_mount_iso $ARG1 # defines $ISOLOOP, to be removed by loop_umount_iso
    if [ $? != 0 ]; then
	error "loop mount failed"
	return 1
    fi

    mkdir -p $dest; cd $dest

    if ! [ -x squashfs-root ]; then
	act "unpacking the squashfs root system"
	unsquashfs $ISOLOOP/live/filesystem.squashfs
	if [ $? != 0 ]; then
	    error "error unsquashing the live filesystem, operation aborted"
	    loop_umount_iso
	    if [ $? != 0 ]; then
		error "failed to free the temporary loop mount, please proceed manually."
		return 1
	    fi
	fi
    else
	act "squashfs root already existing, skipped"
    fi

    if ! [ -x iso-skeleton ]; then
	act "unpacking the iso skeleton"
	mkdir -p iso-skeleton
	rsync --exclude filesystem.squashfs --exclude initrd.img \
	    --inplace -ir $ISOLOOP/* iso-skeleton/
    else
	act "iso skeleton already existing, skipped"
    fi

    if ! [ -x initramfs ]; then
	act "unpacking the init ram system"
	mkdir -p initramfs
	cd -; cd $dest/initramfs
	gunzip < ${ISOLOOP}/live/initrd.img | cpio -i
    else
	act "init ram already existing, skipped"
    fi

    notice "dyne:SDK succesfully created using $ARG1 in $dest"

    loop_umount_iso

    return 0
}

start_xephyr() {
    resolution=$1
    if ! [ $resolution ]; then
	resolution=1024x768
    fi

    pidof Xephyr
    if [ $? != 0 ]; then
	act "starting nested X screen using:"
	act "Xephyr -screen $resolution :1"
	Xephyr -screen $resolution :1 &!
    else
	error "Xephyr already running, operation aborted."
	return 1
    fi
    act "to launch the graphical session use:"
    act "export DISPLAY=localhost:1"
    act "gnome-session (or another window manager)"
    notice "now don't mind the error about fonts, but login with user and password"
    
    return 0
}

enter_sys() {

    if ! [ -r ./squashfs-root ]; then
	error "Invalid SDK: squashfs-root directory not found."
	exit 1
    fi

    if [ $XRES ]; then
        start_xephyr $XRES
    fi

    # bind dev and proc if needed
    mount | grep "${SDK}/squashfs-root/dev/pts" > /dev/null
    if [ $? != 0 ]; then
	mount -o bind /dev/pts ${SDK}/squashfs-root/dev/pts
    fi
    mount | grep "${SDK}/squashfs-root/proc" > /dev/null
    if [ $? != 0 ]; then
	mount -o bind /proc ${SDK}/squashfs-root/proc
    fi
    mount | grep "${SDK}/squashfs-root/var/run" > /dev/null
    if [ $? != 0 ]; then
	mount -o bind /var/run ${SDK}/squashfs-root/var/run
    fi

    # mount | grep "$PFX/$FILE/tmp" > /dev/null
    # if [ $? != 0 ]; then
    # 	mount -o bind /tmp $PFX/$FILE/tmp
    # fi

    cp /etc/resolv.conf ${SDK}/squashfs-root/etc/resolv.conf
    notice "jumping into the live system"
    act "default login is luther, password luther"
    chroot ${SDK}/squashfs-root /bin/login

    if [ $XRES ]; then
        killall Xephyr
    fi
    umount ${SDK}/squashfs-root/dev/pts
    umount ${SDK}/squashfs-root/proc
    umount ${SDK}/squashfs-root/var/run

    # umount $PFX/$FILE/tmp
}

pack_iso() {

    if [ $UID != 0 ]; then return 1; fi

    case $ARG1 in
	init*)
	    if [ -x ${SDK}/initramfs ]; then
		notice "Packing initrd into iso-skeleton/live/initrd.img"
		op=init
	    else
		error "initramfs not found in current SDK, operation aborted."
		return 1
	    fi
	    ;;
	iso*)
	    if [ -x ${SDK}/iso-skeleton ]; then
		notice "Packing the ISO into $dest"
		op=iso
	    else
		error "iso-skeleton not found in current SDK, operation aborted."
		return 1
	    fi
	    ;;
	squash*)
	    if [ -x ${SDK}/squashfs-root ]; then
		notice "Packing the squashfs root into iso-skeleton/live/filesystem.squashfs"
		op=root
	    else
		error "squashfs-root not found in current SDK, operation aborted."
		return 1
	    fi
	    ;;
	*)
	    if  [ -x ${SDK}/iso-skeleton ] && \
		[ -x ${SDK}/squashfs-root ] && \
		[ -x ${SDK}/initramfs ]; then	    
		notice "No argument specified: packing everything into an ISO."
		act "output ISO is named: $dest"
		op=all
	    else
		error "SDK seems incomplete, please specify what you want to pack."
		return 1
	    fi
	    ;;
    esac

    # error 
    res=1

    # SQUASHFS ROOT
    if [ "$op" = "root" ] || [ "$op" = "all" ]; then
	
    # make sure nothing is mounted
	umount $SDK/squashfs-root/dev/pts 2> /dev/null
	umount $SDK/squashfs-root/proc  2>  /dev/null
	umount $SDK/squashfs-root/tmp 2>   /dev/null
    # cleanup tmp
	rm -rf $SDK/squashfs-root/tmp/* 2> /dev/null
	
    # cleanup dns
	rm -f $SDK/squashfs-root/etc/resolv.conf
	echo "nameserver 8.8.8.8" \
	    > $SDK/squashfs-root/etc/resolv.conf
	
	# first update live-scripts from code-repo
	if [ -x $SDK/code-repo/dyneIII/live-init-scripts ]; then
	    cp $SDK/code-repo/dyneIII/live-init-scripts/init \
		$SDK/squashfs-root/usr/share/initramfs-tools/
	    rsync --delete --inplace -ri \
		$SDK/code-repo/dyneIII/live-init-scripts/scripts/* \
		$SDK/squashfs-root/usr/share/initramfs-tools/scripts/
	fi
	mksquashfs $SDK/squashfs-root $SDK/iso-skeleton/live/filesystem.squashfs -noappend
	if [ $? != 0 ]; then
	    error "mksquash failed, operation aborted."
	    return 1
	else
	    res=0; # success
	fi
    fi

    if [ "$op" = "init" ] || [ "$op" = "all" ]; then 

    # pack the initramfs if not present
#    if ! [ -r $SDK/iso-skeleton/live/initrd.img ]; then
	# first update live-scripts from code-repo
	if [ -x $SDK/code-repo/dyneIII/live-init-scripts ]; then
	    cp $SDK/code-repo/dyneIII/live-init-scripts/init $SDK/initramfs/
	    rsync --delete --inplace -ri \
		$SDK/code-repo/dyneIII/live-init-scripts/scripts/* $SDK/initramfs/scripts/
	fi
	# check if we have the busybox powerup
	if ! [ -r $SDK/initramfs/lib/libm.so.6 ]; then
	    act "pimping the busybox in init"
	    cp $SDK/squashfs-root/bin/busybox $SDK/initramfs/bin/
	    for i in `$SDK/squashfs-root/bin/busybox | awk '
    BEGIN {list=0}
    /^Currently/ {list=1; next}
    { if(list>0) print $0 }'`; do
		bb=`echo $i | sed 's/,//'`
		ln -s /bin/busybox $SDK/initramfs/bin/$bb
	    done
	    cp -a $SDK/squashfs-root/lib/libm.so.6 \
		$SDK/squashfs-root/lib/libm-*.so $SDK/initramfs/lib/
	fi
	
	cd $SDK/initramfs
	find | cpio -H newc -o | gzip -9 > $SDK/iso-skeleton/live/initrd.img
	cd -
 #   fi

	act "initrd succesfully packed around the iso-skeleton"
	res=0
    fi

    if [ "$op" = "iso" ] || [ "$op" = "all" ]; then
	notice "baking a bootable ISO in $dest"


    # update ISO skeleton from latest code repository if present in SDK
	if [ -x $SDK/code-repo ]; then
	    act "updating latest source from code-repo"
	    rsync --inplace -ri $SDK/code-repo/dyneIII/iso-skeleton/* $SDK/iso-skeleton/
	fi
	act "starting genisoimage..."

	isofile=${dest}.iso

	genisoimage -r -V "$dest" -cache-inodes -J -l \
	    -b isolinux/isolinux.bin -c isolinux/boot.cat \
	    -no-emul-boot -boot-load-size 4 -boot-info-table \
	    -input-charset iso8859-1 -udf -o ${isofile} ${SDK}/iso-skeleton
	
# omissis: -R -udf 
	if [ $? != 0 ]; then
	    error "an error occurred in genisoimage, operation aborted."
	    res=1
	else
	    notice "iso file packed succesfully:"
	    ls -lh ${isofile}
	    file ${isofile}
	    res=0
	fi
    fi
    return $res
}
    
    
case "$CMD" in
    # execute commands
    create)    check_priv ; create_sdk $@ ;;
    enter)    check_priv ; enter_sys $@ ;;
    pack)    check_priv ; pack_iso $@ ;;
    inject)  check_priv ; inject_init $@ ;;
    startx)  start_x $@ ;;
    *) error "command \"$CMD\" not recognized"
       act "try -h for help"
       exit 1
       ;;
esac

res=$?
if [ $res = 0 ]; then
    notice "operation successful."
fi
exit $res

