#!/bin/sh
# mkinitcpio - modular tool for building an init ramfs cpio image
#
# IMPORTANT: We need to keep a common base syntax here
# because some of these hooks/scripts need to run under
# the klibc shell or even busybox's ash - therefore, the
# following constraints should be enforced:
#   variables should be quoted and bracketed "${SOMEVAR}"
#   inline execution should be done with $() instead of backticks
#   use "x${var}" = "x" to test for nulls/empty strings
#   incase of embedded spaces, quote all path names and string comarpisons
#
#TODO trap and remove FILELIST


# Settings
BASEDIR=""
FILELIST="$(mktemp /tmp/.tmpfilelist.XXXX)"
MESSAGEFILE="$(mktemp /tmp/.message.XXXX)"
KERNELVERSION="$(uname -r)"
FUNCTIONS="functions"
CONFIG="mkinitcpio.conf"
HOOKDIR="hooks"
INSTDIR="install"
MODULE_FILE=""
SAVELIST=""
GENIMG=""
APPEND=""
PRESET=""
MESSAGE=""
PRESETDIR="mkinitcpio.d"
QUIET="y"
SHOW_AUTOMODS="n"

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

usage ()
{
    echo "${APPNAME}: usage"
    echo "  -c CONFIG        Use CONFIG file. default: /etc/mkinitcpio.conf"
    echo "  -k KERNELVERSION Use KERNELVERSION. default: $(uname -r)"
    echo "  -s NAME          Save filelist. default: no"
    echo "  -b BASEDIR       Use BASEDIR. default: /"
    echo "  -g IMAGE         Generate a cpio image as IMAGE. default: no"
    echo "  -a NAME          Append to an existing filelist. default: no"
    echo "  -p PRESET        Build specified preset."
    echo "  -m MESSAGE       Print MESSAGE before passing control to kinit."
    echo "  -v               Verbose output. Default: no"
    echo "  -M               Display modules found via autodetection." 
    echo "  -L               List all available hooks." 
    echo "  -H HOOKNAME      Output help for hook 'HOOKNAME'."
    echo "  -h               This message."
    exit 1
}

while getopts ':c:k:s:b:g:a:p:m:vH:LMh' arg; do
    if [ "${OPTARG:0:1}" = "-" ]; then
        echo "error: optional argument to '-$arg' begins with a '-'"
        echo "  you probably don't want this....aborting."
        usage
    fi
    case "$arg" in
        c) CONFIG="$OPTARG" ;;
        k) KERNELVERSION="$OPTARG" ;;
        s) SAVELIST="y"; FILELIST="$OPTARG" ;;
        b) BASEDIR="$OPTARG" ;;
        g) GENIMG="$OPTARG" ;;
        a) APPEND="y"; SAVELIST="y"; FILELIST="$OPTARG" ;;
        p) PRESET="$OPTARG" ;;
        m) MESSAGE="$OPTARG" ;;
        v) QUIET="n" ;;
        H) source "${INSTDIR}/${OPTARG}";
           echo "Help for hook '${OPTARG}':"
           help
           exit 0 ;;
        L) echo "Available hooks: " 
           for h in ${INSTDIR}/*; do
               echo "   $(basename ${h})"
           done
           exit 0 ;;
        M) SHOW_AUTOMODS="y" ;;
        h|?) usage ;;
        :) echo "${OPTARG} requires a value..."; usage ;;
        *) echo "invalid argument '$arg'"; usage ;;
    esac
done
shift $(($OPTIND - 1))

# use preset $PRESET
if [ -n "${PRESET}" ]; then
    if [ -f "${PRESETDIR}/${PRESET}.preset" ]; then
        # Use -b, -m and -v options specified earlier
        PRESET_MKOPTS="${0}"
        [ -n "${BASEDIR}" ] && PRESET_MKOPTS="${PRESET_MKOPTS} -b ${BASEDIR}"
        [ -n "${MESSAGE}" ] && PRESET_MKOPTS="${PRESET_MKOPTS} -m \"${MESSAGE}\""
        [ "${QUIET}" = "n" ] && PRESET_MKOPTS="${PRESET_MKOPTS} -v"
        # Build all images
        source ${PRESETDIR}/${PRESET}.preset
        for p in ${PRESETS[@]}; do
            echo "==> Building image \"${p}\""
            PRESET_CMD="${PRESET_MKOPTS}"
            eval "PRESET_KVER=\"\${${p}_kver}\""
            eval "PRESET_CONFIG=\"\${${p}_config}\""
            eval "PRESET_IMAGE=\"\${${p}_image}\""
            if [ -n "${PRESET_KVER}" ]; then
                PRESET_CMD="${PRESET_CMD} -k ${PRESET_KVER}"
            elif [ -n "${ALL_kver}" ]; then
                PRESET_CMD="${PRESET_CMD} -k ${ALL_kver}"
            else
                echo "==> No kernel version specified. Skipping image \"${p}\"."
                continue
            fi
            if [ -n "${PRESET_CONFIG}" ]; then
                PRESET_CMD="${PRESET_CMD} -c ${PRESET_CONFIG}"
            else
                echo "==> No configuration file specified. Skipping image \"${p}\"."
                continue
            fi
            if [ -n "${PRESET_IMAGE}" ]; then
                PRESET_CMD="${PRESET_CMD} -g ${PRESET_IMAGE}"
            else
                echo "==> No image file specified. Skipping image \"${p}\"."
                continue
            fi
            echo "==> Running command: ${PRESET_CMD}"
            if eval ${PRESET_CMD}; then
                echo "==> SUCCESS"
            else
                echo "==> FAIL"
            fi
        done
        exit 0
    else
        echo "Preset ${PRESET} does not exist. Exiting."
        exit 1
    fi
fi

# append a trailing / if needed
if [ "${BASEDIR:${#BASEDIR}}" == "/" ]; then
    BASEDIR="${BASEDIR:0:${#BASEDIR}-1}"
fi

MODULEDIR="${BASEDIR}/lib/modules/${KERNELVERSION}"

if [ "x${BASEDIR}" != "x" ]; then
    if [ "${BASEDIR:0:1}" != "/" ]; then
        BASEDIR="$(pwd)/${BASEDIR}"
    elif [ ! -d "${BASEDIR}" ]; then
        echo "base directory '${BASEDIR}' does not exist or is not a directory"
        exit 1
    fi
fi

if [ ! -f "${CONFIG}" ]; then
	echo "config file '${CONFIG}' cannot be found, aborting..."
    exit 1
fi
source "${CONFIG}"

if [ -f "${FILELIST}" -a "x${APPEND}" == "x" ]; then
    if [ "x${SAVELIST}" == "x" ]; then
        rm ${FILELIST}
        touch "${FILELIST}"
    else
        echo "destination file list '${FILELIST}' exists - remove before running"
        exit 1
    fi
else
    touch "${FILELIST}"
fi

BASEDIR=$(echo ${BASEDIR} | tr -s /)
MODULEDIR=$(echo ${MODULEDIR} | tr -s /)

source "${FUNCTIONS}"

if [ "${SHOW_AUTOMODS}" = "y" ]; then
    echo "Modules autodetected:"
    source "${INSTDIR}/autodetect"
    install
    cat "${MODULE_FILE}"
    exit 0
fi

echo ":: Begin build"
#parse 'global' hook, as defined in ${CONFIG}
parse_hook

for hook in $HOOKS; do
    unset MODULES
    unset BINARIES
    unset FILES
    install () { msg "${hook}: no install function..."; }
    # Deprecation check
    # A hook is considered deprecated if it is a symlink
    # within $INSTDIR.
    if [ -L "${INSTDIR}/${hook}" ]; then
        newhook="$(readlink -ne "${INSTDIR}/${hook}")"
        if [ -n "${newhook}" -a "${INSTDIR}/$(basename ${newhook})" -ef "${newhook}" ]; then
            newhook="$(basename ${newhook})"
            echo "   -------------------------------------------------------------------"
            echo "   WARNING: Hook \"${hook}\" is deprecated."
            echo "            Replace it with \"${newhook}\" in your configuration file."
            echo "   -------------------------------------------------------------------"
            hook="${newhook}"
        fi
    fi
    if grep "install" "${INSTDIR}/${hook}" >/dev/null 2>&1; then
        source "${INSTDIR}/${hook}"
        echo ":: Parsing hook [${hook}]"
        install
        parse_hook
    else
        die "Hook '${hook}' can not be found."
    fi
done

if [ "${HAS_MODULES}" == "y" ]; then
    echo ":: Generating module dependencies"
    for mod in $(grep "file /lib/modules/${KERNELVERSION}" ${FILELIST} | cut -d' ' -f2); do
        dir=$(dirname "${mod}")
        mkdir -p "/tmp/${dir}"
        cp "${BASEDIR}${mod}" "/tmp/${dir}/"
    done
    depmod -b /tmp ${KERNELVERSION}
    add_file "/tmp/lib/modules/${KERNELVERSION}/modules.dep"     "/lib/modules/${KERNELVERSION}/modules.dep"
    add_file "/tmp/lib/modules/${KERNELVERSION}/modules.alias"   "/lib/modules/${KERNELVERSION}/modules.alias"
    add_file "/tmp/lib/modules/${KERNELVERSION}/modules.symbols" "/lib/modules/${KERNELVERSION}/modules.symbols"
fi

status=0
if [ "x$GENIMG" != "x" ]; then
    echo -n ":: Generating image '${GENIMG}'..."
    if ! gen_init_cpio ${FILELIST} | gzip -9 > "${GENIMG}"; then
        echo "FAILED"
        status=1
    else
        echo "SUCCESS"
        status=0
    fi

    if [ "x${SAVELIST}" == "x" ]; then
        rm ${FILELIST}
    fi
    rm ${MESSAGEFILE}
else
    echo ":: Dry run complete, use -g IMAGE to generate a real image"
fi

#cleanup - we should probably trap this...
isempty () { [ $(ls -1 "${1}" | wc -l) -eq 0 ]; }
[ -e "${MODULE_FILE}" ] && rm "${MODULE_FILE}"
if [ -d /tmp/lib/modules/${KERNELVERSION} ]; then
    rm -rf /tmp/lib/modules/${KERNELVERSION}
    isempty /tmp/lib/modules && rm -rf /tmp/lib/modules
    isempty /tmp/lib && rm -rf /tmp/lib
fi
exit $status
#vim:set ft=sh ts=4 sw=4 noet:
