#!/bin/bash
# barium helper scripts
# author: rosalinux.ru: betcher_
VARS="INDEX UNION UNIONFS CHANGES SPACE LINDEX"
CONTANER='nspawn'
MASK='base'
EXCLUDE=''
erase='yes' # "yes" need to preserve whiteouts by default

if [ -f $(dirname $0)/lib ] ;  then
    . $(dirname $0)/lib
else
    . $(which lib) || exit 1
fi
HLP() {
    echo "Usage:    $(basename $0) -o module_name.xzm <keys> --command <command to execute in the chroot>"
    echo "          $(basename $0) -o module_name.xzm <keys> -- <command> (same as --command)"
    echo "          $(basename $0) -o module_name.xzm <keys> --script <script name>"
    echo "          $(basename $0) -o module_name.xzm <keys> --project <project dir>"
    echo
    echo "$(basename $0) keys:"
    echo "-h | --help - this help"
    echo "-o | --name, fullname or dir for the module"
    echo "-r | --reassembly - set flag reassembly (use with --command)"
    echo "-m | --mask - mask (grep -E format) to select modules to build union rootfs"
    echo "-p | --parents - read parent modules list from module abd use this list as MASK"
    echo "--remove-wh  - remove whiteouts for module"
    echo "--mask clean - special mask value \"clean\" to choose base modules only (default)"
    echo "--mask all   - special mask value \"all\" to choose all activated modules"
    echo "--container  - chroot app to use. Avaliable: chroot, nspawn"
    echo "--bind       - list of /host/dir:/chroot/dir separated by comma to mount bind"
    echo "               before the chroot command"
    echo "
--project - preferred option for building complex modules.
The directory for the --project parameter must contain the build.c2m executable script
and all files necessary for building the module.
Additionally, can be added a prepack.c2m, script which run in the module directory before packing.
And exclude.c2m, it is a list of exclusions for mksquashfs, wildcards are allowed in the list.
    "
    exit
}

updated_base_mods() {
    upd_dir="$(find_distrpath).upd"
    [ -d "${upd_dir}/base" ] && find "${upd_dir}/base" -type f -name "*.$EXT" |\
    grep -E "$MASK" | paste -sd ','
}

[ "$1" == "" -o "$1" == "--help" ] && HLP
root_only

mod_path=${SYSMNT}/bundles
TMPDIR=${SYSMNT}/tmp-chroot2mod

mkdir -p ${TMPDIR}/0
lastN=$(ls -1 ${TMPDIR} |sort -n |tail -n1)
N=$(( $lastN + 1 ))
buildDir=${TMPDIR}/$N

crash_exit() {
    destroy_union $INDEX
    destroy_layers $LINDEX
    rm -rf $buildDir
    if [ "$2" -ne 0 ]; then
        echo -ne '\033[0;31m'
        echo "$1"
        echo -ne '\033[0m\n'
        [ "$3" ] || exit $2
    fi
}

cmdline="$@"
while [ -n "$1" ] ; do
    case "${1}" in
    "-h" | "--help") HLP ;;
    "-X" ) X="yes" ;;
    "-r" | "--reassembly" ) REASSEMBLY='yes' ;;
    "-o" ) shift ; NAME="$1" ;;
    "-m" | "--mask") shift ; MASK="$1" ;;
    "-p" | "--parents") shift ; MASK=$(parents2mask $1) ;;
    "-c" | "--contaner" ) shift ; CONTANER="$1" ;;
    "--bind" ) shift ; BIND="$1" ;;
    "--script" ) shift ; SCRIPT="$1" ;;
    "--project" ) shift ; PROJECT="$1"
                  [ $NAME ] || NAME=$(basename $PROJECT).xzm;;
    "--upd" ) USEUPD='--upd' ;;
    "--remove-wh" ) erase='no' ;;
    "--" | "--command" ) shift ; COMMAND="$@" ; break ;;
    "-"*[A-Za-z]*)  echo "$(basename "$0"): invalid option ${1}"
                    exit 1 ;;
    *) modules="${1} ${modules}"
    esac
shift
done

# rebuild modules
if  $(file $(echo $modules |awk '{print $1}') 2>/dev/null | grep -q quashfs); then
    for rmod in $modules ; do
    rmod=$(realpath $rmod)
    if [ $(unsquashfs -l $rmod | grep  .*/var/lib/chroot2mod/.*/reassembly |wc -l) -ne 1 ]  ; then
            exitmsg "Module $rmod is not chroot2mod reassembly" 1 noexit
            continue
    fi
    mkdir -p $buildDir
    echo "rebuilding $rmod in $buildDir"
    unsquashfs -d ${buildDir}/unsqfs "$rmod" -e /var/lib/chroot2mod -n >/dev/null
    mv $buildDir/unsqfs/var/lib/chroot2mod/* ${buildDir}/
    rm -fr $buildDir/unsqfs
    self=$(realpath $0)
    CPWD=$(dirname $rmod)
    pushd $buildDir
    for mod in $(find ./ -type d -name "??*"); do
        [ -x "$mod/build.c2m" ] && SCRIPT="--script $(realpath $mod/build.c2m )"
        echo :::"$self $USEUPD $(echo $(cat $mod/cmdline) $SCRIPT)":::
        $self $USEUPD $(echo $(cat $mod/cmdline) $SCRIPT ) | tee ./chroot2mod.out
        module=$(cat ./chroot2mod.out | tail -n1)
        mv $rmod ${CPWD}/$(basename $module).old 2>/dev/null
        mv $module ${CPWD}/
    done
    popd
    rm -rf $buildDir
    newmods="$(echo $CPWD/$(basename $module)) $newmods"
    done
    echo "$newmods"
    exit
fi

if ! [ "$NAME" ] ; then
    echo "Please enter the name for module"
    read NAME
fi

TARGETD="$(realpath $(dirname $NAME))"
NAME="$(basename $NAME)"

[ "$MASK" == "clean" -o "$MASK" == "base" ] && MASK="${SYSMNT}/bundles/[0-9][0-9]-"
[ "$MASK" == "all" ] && MASK="."

if [ "$USEUPD" == '--upd' ] ; then
    MASK="[0-9][0-9]-"
    list_base_mods=$(updated_base_mods)
    echo -e "Base mods list:\n$list_base_mods"
fi

if [ "$USEUPD" == '--upd' ] && [ "$list_base_mods" ]; then
    layers=$(prepare_layers LIST="$list_base_mods")
else
    layers=$(prepare_layers MASK="$MASK")
fi

for a in $(make_union "$layers"); do
    echo "$VARS" | grep -q "$(echo "$a" | cut -f1 -d=)" && eval "$a"
done
trap "crash_exit 'Aborted!!!' 3" 1 2 3 15

[ -d $UNION ] || exitmsg "Can't mount $UNIONFS" ${LINENO}

if [ "$CONTANER" = 'chroot' ] ; then
    mkdir -p $UNION/{dev,proc,sys,tmp}
    for tm in {dev,proc,sys}; do
        mount -o bind /$tm $UNION/$tm
    done
fi

mkdir -p $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")
date > $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/date
echo  "${cmdline/--upd/}" > $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/cmdline
[ "$REASSEMBLY" ] && : > $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/reassembly

if [ "$SCRIPT" ] ; then
    cp  $SCRIPT  $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/build.c2m
    chmod +x $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/build.c2m
    sed -i 's/--script.*$//' $UNION/var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/cmdline
fi

if [ "$PROJECT" ] ; then
    BIND="$(realpath $PROJECT)::/var/tmp/$(basename $PROJECT),$BIND"
    COMMAND=/var/tmp/$(basename $PROJECT)/build.c2m
fi

if [ "$BIND" ] ; then
bind_dirs=''
binds="$(echo "$BIND" |sed 's/[\,]/ /g')"
for bnd in $binds ; do
    extdir=$(echo $bnd |sed 's/::/#/' |cut -d "#" -f1)
    indir="$UNION/$(echo $bnd |sed 's/::/#/' |cut -d "#" -f2)"
    mkdir -p "$indir"
    mount -o bind "$extdir" "$indir"
    bind_dirs="$bind_dirs $indir"
done
fi

if [ "$X" ] ; then
    if ! xhost |grep -q "LOCAL:" ; then
        xhost +local:
        remove_X_perm=yes
    fi
fi

if [ "$CONTANER" == "nspawn" ] ; then
    ps aux | md5sum  | awk '{print $1}' > $UNION/etc/machine-id #  nspawn does not works without machine-id
    [ "$SCRIPT" ] || systemd-nspawn $BOOT -D "$UNION"   -M ${NAME%.$EXT} --console=interactive --setenv=DISPLAY="$DISPLAY" --bind=/tmp/.X11-unix:/tmp/.X11-unix $COMMAND
    [ "$SCRIPT" ] && systemd-nspawn $BOOT -D "$UNION"   -M ${NAME%.$EXT} --console=interactive --setenv=DISPLAY="$DISPLAY" --bind=/tmp/.X11-unix /var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/build.c2m
elif [ "$CONTANER" == "chroot" ] ; then
    [ "$SCRIPT" ] || chroot $UNION env -i DISPLAY="$DISPLAY" $COMMAND
    [ "$SCRIPT" ] && chroot $UNION env -i DISPLAY="$DISPLAY" /var/lib/chroot2mod/$(basename "${NAME%.$EXT}")/build.c2m
else
    NOMOD=yes
    echo "Wrong contaner type, must be \"chroot\" or \"nspawn\""
fi

[ "$remove_X_perm" ] && xhost -local:

if [ "$CONTANER" = 'chroot' ] ; then
    for tm in {dev,proc,sys}; do
        umount $UNION/$tm
    done
fi

set_order "$UNION" "$CHANGES" "chroot2mod" "${NAME%.$EXT}"

umount $UNION

clear_CHANGES

if [ "$PROJECT" ] ; then
    [ -f ${PROJECT}/exclude.c2m ] && EXCLUDE="-ef $(realpath ${PROJECT}/exclude.c2m)"
    if [ -x ${PROJECT}/prepack.c2m ] ; then
    prepack_script=$(realpath ${PROJECT}/prepack.c2m)
    pushd $CHANGES
    $prepack_script
    popd
    fi
fi

[ $NOMOD ] ||   mksquashfs $CHANGES ${TARGETD}/${NAME}  $EXCLUDE $SQFSOPT  -noappend  -wildcards && MODULE="$(readlink -e ${NAME})"

[ "$bind_dirs" ] && for dir in $bind_dirs ; do
    umount "$dir"
done

destroy_union $INDEX
destroy_layers $LINDEX && rm -rf "$buildDir"

[ "$MODULE" ] && echo $MODULE
