#!/bin/bash
# barium helper scripts
# author: rosalinux.ru: betcher_
VARS="INDEX UNION CHANGES SPACE LINDEX"
if [ -f $(dirname $0)/lib ] ;  then
    . $(dirname $0)/lib
else
    . $(which lib) || exit 1
fi

HLP() {
    echo "Usage:  $(basename $0) <$(basename $0) keys> <packages list>  -o mod.xzm --dnf <keys for dnf>"
    echo "             $(basename $0) module1.xzm module2.xzm module3.xzm  < -l -d /path>-  rebuild modules"
    echo "$(basename $0) keys:"
    echo "-h | --help - this help"
    echo "-o - name, fullname or dir for the module"
    echo "-m | --mask - mask (grep -E format) to select modules where $(basename $0) will search rpm db"
    echo "-p | --parents - read parent modules list from module abd use this list as MASK" 
    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 "-s | --sysdnf - using system dnf configuration and cache"
    echo "-r | --rpmdb -  save /var/lib/rpm/* in module (rpm data base)"
    echo "-u | --dnfdb - save dnf database in module"
    echo "-e | --erase - create a module with whiteouts after dnf remove packages"
    echo "-b | --rebuildable - save in module local rpms, it need to rebuild module"
    echo "-f | --fast - fast compression alg"
    echo "--steps  - run script in \"step by step\" mode"
    echo " Next parameter works with rebuild module too:"
    echo "-d | --bulddir  - working directory, default dir is in tmpfs."
    echo "-l | --load - mount the module after creation"
    echo "-i | --info  - show module info"
    echo "-F | --force - do not ask something"
    exit
}

[ "$1" == "" -o "$1" == "--help" ] && HLP
if [ $(id -un) != "root" ] ; then
    echo  "You must be root !!!"
    exit 1
fi

MASK="base"                  # default mask (grep format) for modules in $mod_path to be mounted in new aufs
FORCE='no'                   # ask or not
dnfpars="-yb --color=never"  # default pars for dnf
packages=""                  # list of packages, all parameters from dnf2mod cmdline without "-" or "--" are packages
rpmdb="no"                   # save or not rpm database in module
dnfdb="no"                   # update and save or not dnf db before install packages
erase="no"                   # make module with installed packages or deleted packages (wh aufs shadows)
result="-1"                  # result must be changed to zero by script
rebuildable="no"             # save or not local rpm packages in module
argslist="$@"
del_prefix="hidden_"
create='no'

#Work dirs
export PATH=/usr/lib/rosa-rw/scripts:$PATH

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


mod_path=${SYSMNT}/bundles
# to change default values via configs

. /usr/lib/rosa-rw/os-config
. /etc/ROSA-RW/config

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
}

clear_db() {
    # save or not databases
    [ "$rpmdb" == "no" ] && rm -rf "$CHANGES"/var/lib/rpm
    [ "$dnfdb" == "no" ] && rm -rf "$CHANGES"/var/lib/dnf
}


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 ','
}

#get packages list and dnf2mod parameters
separ=''
packages=''
arglist="$@"
while [ -n "$1" ] ; do
    case "${1}" in
    "-o" ) shift ; NAME="$1" ;;
    "-m" | "--mask") shift ; MASK="$1" ;;
    "-p" | "--parent") shift ; MASK=$(parents2mask $1) ;;
    "-d" | "--builddir" ) shift ; buildDir="$1" ;;
    "-h" | "--help") HLP ;;
    "-s" | "--sysdnf") sysdnf="yes" ;;
    "-l" | "--load") load="yes" ;;
    "-r" | "--rpmdb") rpmdb="yes" ;;
    "-u" | "--dnfdb") dnfdb="yes" ;;
    "-e" | "--erase") erase="yes" ;;
    "-i" | "--info") info="yes" ;;
    "-f" | "--fast") SQFSOPT="$SQFSOPT_fast" ;;
    "-b" | "--rebuildable") rebuildable="yes" ;;
    "-F" | "--force" ) FORCE='yes' ;;
    "--upd" ) USEUPD='--upd' ;;
    "--steps") steps="yes" ;;
    "--dnf" ) shift ; dnfpars="$@" ; break ;;
    "-"*[A-Za-z]*)
        echo "$(basename "$0"): invalid option -- '$(echo ${1} | tr -d '-')'" >&2
        exit 1 ;;
    *)  packages="${packages}${separ}$1" ; separ=' ' ;;
    esac
    shift
done

# rebuild modules
if  $(file $(echo $packages |awk '{print $1}') 2>/dev/null | grep -q quashfs); then
    for rmod in $packages ; do
        if ! unsquashfs -l $rmod | grep -q .*/var/lib/dnf2mod/.  ; then
            echo "Warning! Module $rmod was made by another program"
            continue
        fi
    mkdir -p $(dirname $buildDir)
    echo "$rmod processing..."
    self=$(realpath $0)
    unsquashfs -d $buildDir "$rmod" -e /var/lib/dnf2mod >/dev/null
    mv $buildDir/var/lib/dnf2mod/* ${buildDir}/ 2>/dev/null
        rm -fr $buildDir/var
    CPWD=$(pwd)
        pushd $buildDir
    for mod in $(find ./ -type d -name "??*"); do
        $self  $(echo $(cat $mod/cmdline)) "$USEUPD" | tee ./dnf2mod.out
        module=$(cat ./dnf2mod.out | tail -n1)
        mv $CPWD/$(basename $module) $CPWD/$(basename $module).old 2>/dev/null
        mv $module ${CPWD}/
        [ -f $CPWD/$(basename $module) ] && newmods="$CPWD/$(basename $module)\n$newmods"
    done
    popd >/dev/null
    rm -rf $buildDir
    done
    echo -e "$newmods"
    exit
fi

#get path and name for out module
if [ $NAME ]; then
    if [ $(echo $NAME | awk -F. '{print $NF}') == "xzm" ]; then
        PNAME=$(dirname $NAME)
        NAME=$(basename $NAME)
        del_prefix=""
    else
    PNAME="$NAME"
        unset NAME
    fi
fi
[ $NAME ] || NAME=$(echo $packages | awk '{print $1}').$EXT
[ $PNAME ] || PNAME=$(dirname $NAME)
[ -d $PNAME ] || crash_exit "Directory \"$PNAME\" is not exists" 2

[ "$MASK" == "clean" -o "$MASK" == "base" ] && MASK="$DEFMASK" 
[ "$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

[ $UNION ] || exitmsg "Can't mount $UNIONFS" ${LINENO}
mkdir -p $UNION/{dev,proc,sys,tmp}
for tm in {dev,proc,sys}; do
    mount -o bind /$tm $UNION/$tm
done

if [ "$sysdnf" ] ; then
    mkdir -p ${UNION}/etc/{dnf,yum.repos.d} ${UNION}/var/cache/dnf
    mount -o bind /etc/dnf ${UNION}/etc/dnf
    mount -o bind /etc/yum.repos.d ${UNION}/etc/yum.repos.d
    mount -o bind /var/cache/dnf ${UNION}/var/cache/dnf
fi

mkdir -p $buildDir
#get rpms list before installing, need to generate rpm list in /var/lib/rpm/modules/mod_name
rpm -qa -r $UNION > $buildDir/rpm_qa_before

[ "$steps" ] && read -p "next step - dnf" ecode
if [ "$ecode" == "exit" ]; then
    umount ${UNION}/etc/{dnf,yum.repos.d}
    destroy_union $INDEX
    destroy_layers $LINDEX
    exit
fi

if [ "$erase" == "no" ]; then
    for pack in $packages; do
        #try to delete package before installing, it need to install package again
        rpm -q "$pack" >/dev/null 2>&1 && /usr/bin/dnf -q remove --installroot="$UNION" -y $pack 2>/dev/null
    done
    [ "$dnfdb" == "yes" ] && /usr/bin/dnf  --refresh --installroot "$UNION"
    /usr/bin/dnf install --installroot "$UNION"  $packages $dnfpars
    result=$?
    action=install
else
    #delete packages, it need to make module width overlay whiteouts
    /usr/bin/dnf remove --installroot "$UNION" $packages $dnfpars
    result=$?
    action=erase
fi

[ "$dbfdb" == 'no' ] && /usr/bin/dnf clean all --installroot "$UNION"

#get rpms list after installing
rpm -qa -r $UNION >$buildDir/rpm_qa_end
clear_CHANGES
clear_db

#save info into the module
mkdir -p ${CHANGES}/var/lib/dnf2mod/$(basename "${NAME%.$EXT}")
mkdir -p ${CHANGES}/var/lib/rpm/modules/
date > ${CHANGES}/var/lib/dnf2mod/$(basename "${NAME%.$EXT}")/date
cat ${SYSMNT}/layer-base/0/VERSION > ${CHANGES}/var/lib/dnf2mod/$(basename "${NAME%.$EXT}")/os-release
echo -e "$(cat $buildDir/rpm_qa_* | sort | uniq -d)\n$(cat $buildDir/rpm_qa_end)" | sort | uniq -u >${CHANGES}/var/lib/rpm/modules/$(basename "${NAME%.$EXT}")
sed -i 's/\(.*\)/\1.rpm/g' ${CHANGES}/var/lib/rpm/modules/$(basename "${NAME%.$EXT}")
set_order $UNION ${CHANGES} dnf2mod $(basename "${NAME%.$EXT}")

# change all path in $@  to local paths, need to rebuild
last="_"
for a in $arglist; do
    if [ -f "$a" -o "$(echo $a | awk -F. '{print $NF}')_" == "${EXT}_" ]; then
        basename $a
    elif [ -d "$a" -a $last == "-o" ]; then
        echo "./"
    elif [ "$last" == "-d" -o "$last" == "--builddir" ] ; then
        true # drop builddir value
    else
        [ "$a" != "-l" -a "$a" != "--load" -a "$a" != "-d" -a "$a" != "--builddir"  -a "$a" != "--upd" ] \
        && echo "$a" # drop --load, --builddir parameters
    fi
    last="$a"
done > ${CHANGES}/var/lib/dnf2mod/$(basename "${NAME%.$EXT}")/cmdline

#save local rpms in module
if [ "$rebuildable" == "yes" ]; then
    for b in $@; do
        [ -f "$b" -a "$(echo $b | awk -F. '{print $NF}')_" == "rpm_" ] && cp $b $CHANGES/var/lib/dnf2mod/
    done
fi

[ "$steps" ] && read -p "next step - mksquashfs" ecode
if [ "$ecode" == "exit" ]; then
    umount ${UNION}/etc/{dnf,yum.repos.d}
    destroy_union $INDEX
    destroy_layers $LINDEX
    exit
fi

if [ "$result" -eq 0 -a "$erase" == "no" ]; then
    mksquashfs $CHANGES ${PNAME}/${NAME} $SQFSOPT -noappend  && MODULE="$(readlink -e ${PNAME}/${NAME})" && create=OK
elif [ "$result" -eq 0 ]; then
    mksquashfs $CHANGES ${PNAME}/${del_prefix}${NAME} $SQFSOPT -noappend && MODULE="$(readlink -e ${PNAME}/${del_prefix}${NAME})" && create=OK
elif [ "$FORCE" == 'no' ] ; then
    echo "Error $action packages: $packages"
    echo "Size of module source dir: $(du -ch $rw_br | tail -n1 | cut -f 1)"
    echo "Do you want to create the module, despite errors? [Y/N]"
    read aaa
    [ -z "$aaa" ] || aaa=no
    if [ $aaa == "Y" -o $aaa == "y" ]; then
        mksquashfs $CHANGES ${PNAME}/${NAME} -noappend $SQFSOPT && MODULE="$(readlink -e ${PNAME}/${NAME})" && create=OK
    fi
else
    echo "Error $action packages: $packages"
fi
[ "$sysdnf" ] && umount ${UNION}/etc/{dnf,yum.repos.d}
destroy_union $INDEX
destroy_layers $LINDEX
rm -rf "$buildDir"

if [ "$create" == 'OK' ]; then
    echo "$MODULE"
else
    exit 5
fi
