#!/bin/sh

CPIOCONFIG="$(pwd)/archiso-mkinitcpio.conf"
DEF_CONFIG_DIR="$(pwd)/default-config"
PKGFILE="$(pwd)/packages.list"
PKGLIST=""
QUIET="y"
FORCE="n"

command_name=""
work_dir=""
isoname=""

APPNAME=$(basename "${0}")

# usage: usage <exitvalue>
usage ()
{
    echo "usage ${APPNAME} [options] command <command options>"
    echo " general options:"
    echo "    -f               Force overwrite of working files/squashfs image/iso"
    echo "    -i CPIO_CONFIG   Use CONFIG file for mkinitcpio. default: ${CPIOCONFIG}"
    echo "    -P PKGFILE       File with list of packages to install. default: ${PKGFILE}"
    echo "    -p PACKAGE       Additional package to install, can be used multiple times"
    echo "    -v               Enable verbose output."
    echo "    -h               This message."
    echo " commands:"
    echo " install <working dir>        : where to build the ISO root"
    echo " squash <working dir>         : generate a squashfs image of the ISO root"
    echo " iso <working dir> <iso name> : build an ISO from the working directory"
    echo " all <working dir> <iso name> : perform all of the above, in order"
    exit $1
}

while getopts 'i:P:p:fvh' arg; do
    case "${arg}" in
        i) CPIOCONFIG="${OPTARG}" ;;
        P) PKGFILE="${OPTARG}" ;;
        p) PKGLIST="${PKGLIST} ${OPTARG}" ;;
        f) FORCE="y" ;;
        v) QUIET="n" ;;
        h|?) usage 0 ;;
        *) echo "invalid argument '${arg}'"; usage 1 ;;
    esac
done

# do UID checking here so someone can at least get usage instructions
if [ "$EUID" != "0" ]; then
    echo "error: This script must be run as root."
    exit 1
fi

shift $(($OPTIND - 1))
echo "ARGS: $@"

[ $# -le 1 ] && usage 1

command_name="${1}"
case "${command_name}" in
    install) work_dir="${2}" ;;
    squash) work_dir="${2}" ;;
    iso) work_dir="${2}"; isoname="${3}" ;;
    all) work_dir="${2}"; isoname="${3}" ;;
    *) echo "invalid command name '${command_name}'"; usage 1 ;;
esac

[ "x${work_dir}" = "x" ] && (echo "please specify a working directory" && usage 1)

isoroot="${work_dir}/iso"
instroot="${work_dir}/install"

_kversion ()
{
    source ${instroot}/etc/mkinitcpio.d/kernel26.kver
    echo ${ALL_kver}
}

# usage: _pacman <packages>...
_pacman ()
{
    local ret
    if [ "${QUIET}" = "y" ]; then
        mkarchroot -f ${instroot} $* 2>&1 >/dev/null
        ret=$?
    else
        mkarchroot -f ${instroot} $*
        ret=$?
    fi
    if [ $ret -ne 0 ]; then
        exit 1
    fi
}

# usage: install_pkgfile <packagesfile>
install_pkgfile ()
{
    if [ -e "${1}" ]; then
        toinstall=""
        while read pkg; do
            toinstall="${toinstall} ${pkg}"
        done < ${1}
        _pacman "${toinstall}"
    else
        echo "error: Package file '${1}' does not exist, aborting."
        exit 1
    fi
}

# Go through the main commands in order. If 'all' was specified, then we want
# to do everything. Start with 'install'.
if [ "${command_name}" = "install" -o "${command_name}" = "all" ]; then
    echo "====> Installing/building ISO root"
    if [ -e "${work_dir}" -a "${FORCE}" = "n" ]; then
        echo "error: Working dir '${work_dir}' already exists, aborting."
        exit 1
    fi

    mkdir -p "${isoroot}"
    mkdir -p "${instroot}"

    echo "Installing packages..."
    echo " Installing packages from '${PKGFILE}'"
    install_pkgfile "${PKGFILE}"

    for pkg in ${PKGLIST}; do
        echo "   Installing package '${pkg}'"
        _pacman "${pkg}"
    done

    echo "Updating kernel module dependancies"
    kernelver=$(_kversion)
    depmod -a -b "${instroot}" -v "${kernelver}" -F "${instroot}/boot/System.map26" >/dev/null
    # remove the initcpio images that were generated for the host system
    find "${instroot}/boot" -name *.img -delete

    echo "Applying default configuration for the Arch ISO"
    cp -rfa ${DEF_CONFIG_DIR}/* "${instroot}"

    echo "Copyright (C) 2007, Arch Linux (Judd Vinet)" > "${instroot}/etc/copyright"

    echo "Creating initial device nodes"
    rm -f "${instroot}/dev/console" "${instroot}/dev/null" "${instroot}/dev/zero"
    mknod -m 644 "${instroot}/dev/console" c 5 1
    mknod -m 666 "${instroot}/dev/null" c 1 3
    mknod -m 666 "${instroot}/dev/zero" c 1 5 

    echo "Creating default home directory"
    install -d -o1000 -g100 -m0755 "${instroot}/home/arch"

    # Cleanup
    echo "Cleaning up ISO root files..."
    find "${instroot}" -name *.pacnew -name *.pacsave -name *.pacorig -delete

    # delete a lot of unnecessary cache/log files
    kill_dirs="var/abs var/cache/man var/cache/pacman var/log/* var/mail tmp/* usr/include initrd"
    for x in ${kill_dirs}; do
        if [ -e "${instroot}/${x}" ]; then
            rm -rf "${instroot}/${x}"
        fi
    done

    # delete static libraries
    find "${instroot}/lib" -name *.a -delete
    find "${instroot}/usr/lib" -name *.a -delete

    # pacman DBs are big, delete all sync dbs
    for d in ${instroot}/var/lib/pacman/*; do
        [ "$(basename ${d})" != "local" ] && rm -rf "${d}"
    done

    if [ -e "${instroot}/boot" ]; then
        rm -rf "${isoroot}/boot"
        mv "${instroot}/boot" "${isoroot}"
    fi
fi

# Squash is the next step.
if [ "${command_name}" = "squash" -o "${command_name}" = "all" ]; then
    echo "====> Generating SquashFS image"
    imagename="${isoroot}/archlive.sqfs"
    if [ -e "${imagename}" ]; then
        if [ "${FORCE}" = "y" ]; then
            echo -n "Removing old SquashFS image..."
            rm "${imagename}"
            echo "done."
        else
            echo "error: SquashFS image '${imagename}' already exists, aborting."
            exit 1
        fi
    fi

    echo "Creating squashfs image. This may take some time..."
    start=$(date +%s)
    if [ "${QUIET}" = "y" ]; then
        mksquashfs "${instroot}" "${imagename}" >/dev/null
    else
        mksquashfs "${instroot}" "${imagename}"
    fi
    minutes=$(echo $start $(date +%s) | awk '{ printf "%0.2f",($2-$1)/60 }')
    echo "Image creation done in $minutes minutes."
fi

# Finally, make the iso.
if [ "${command_name}" = "iso" -o "${command_name}" = "all" ]; then
    echo "====> Making ISO image"
    [ "x${isoname}" = "x" ] && (echo "ISO image name must be specified" && usage 1)
    if [ -e "${isoname}" ]; then
        if [ "${FORCE}" = "y" ]; then
            echo "Removing existing ISO image..."
            rm -rf "${isoname}"
        else
            echo "error: ISO image '${isoname}' already exists, aborting."
            exit 1
        fi
    fi
    if [ ! -e "${CPIOCONFIG}" ]; then
        echo "error: mkinitcpio config '${CPIOCONFIG}' does not exist, aborting."
        exit 1
    fi

    kernelver=$(_kversion)
    basedir=${instroot}
    [ "${instroot:0:1}" != "/" ] && basedir="$(pwd)/${instroot}"
    echo "Generating initcpio for ISO..."
    if [ "${QUIET}" = "y" ]; then
        mkinitcpio -c "${CPIOCONFIG}" -b "${basedir}" -k "${kernelver}" -g "${isoroot}/boot/archlive.img" >/dev/null
        ret=$?
    else
        mkinitcpio -c "${CPIOCONFIG}" -b "${basedir}" -k "${kernelver}" -g "${isoroot}/boot/archlive.img"
        ret=$?
    fi
    if [ $ret -ne 0 ]; then
        echo "error: initcpio image creation failed..."
        exit 1
    fi

    cp ${instroot}/usr/lib/grub/i386-pc/* "${isoroot}/boot/grub"

    echo "Creating ISO image..."
    q=""
    [ "${QUIET}" = "y" ] && qflag="-q"
    mkisofs ${qflag} -r -l -b "boot/grub/stage2_eltorito" -uid 0 -gid 0 -no-emul-boot \
        -boot-load-size 4 -boot-info-table -publisher "Arch Linux <archlinux.org>" \
        -input-charset=UTF-8 -p "prepared by $NAME" -A "Arch Linux Live/Rescue CD" \
        -copyright /etc/copyright -o "${isoname}" "${isoroot}"
fi

# vim:ts=4:sw=4:et: