#!/bin/bash
# barium helper scripts library
# author: rosalinux.ru: betcher_
# email: a.betkher@rosalinux.ru
# version: 0.1

# to use losetup by user
PATH="/sbin/:/usr/sbin:$PATH"

. /etc/initvars
COPY2RAMDIR=${SYSMNT}/copy2ram/
BUNDLESDIR=${SYSMNT}/bundles/
CHANGESDIR=${SYSMNT}/changes/
METAINFODIR=/var/lib/packages
EXT=xzm

color_red='\033[0;31m'
color_green='\033[0;32m'
color_yellow='\033[0;33m'
color_blue='\033[0;34m'
color_default='\033[0m'

SQFSOPT="-b 512K -comp xz"
SQFSOPT_fast="-comp lz4"

if echo $SQFSOPT |grep -q xz; then
	if uname -m | grep -q -e "86" -e "32" -e "64" ;then
		SQFSOPT="$SQFSOPT -Xbcj x86"
	else 
		SQFSOPT="$SQFSOPT -Xbcj x86,arm"
	fi
fi

CFG_DIR="/etc/barium" 			# папка с конфигами для getmod
PUBKEYS="$CFG_DIR/keys"			# папка для доверенных публичных ключей
mirror_list="$CFG_DIR/mirror.lst"	# список зеркал
ALIASCFG="$CFG_DIR/aliases.cfg"		# файл с алиасами для нечеткого поиска
WORK_DIR="/var/tmp/barium"
TMPUNION=${SYSMNT}/tmp_union
TMPSQFS=${SYSMNT}/tmp_sqfs

#user config
[ -f ${CFG_DIR}/barium.cfg ] && . ${CFG_DIR}/barium.cfg

HLP() {
	# this help page
	#
echo "$(basename $0) - library for barium utils"
echo "Usage: 
	source /path/$(basename $0) func_name args	
or
	barium $(basename $0) func_name args

#############################################################

"
cat $0 |grep -E -A2 '^[a-z,A-Z,_]+\(\) {$' | \
sed -e 's/(.*$//' -e 's/^--$//' -e 's/#/*/'   
exit
}

action() { # hide from --help
	action=$@
	echo "==> $action"
}

echo_exit() { # hide from --help
	echo "$@"
	[ "$2" -a "$2" -ne 0 ] && exit $2  
}
getPin () {
	# $1 - lehth of pincode
	# return - random pin
	lenth=$1
	a=''
	while [ $(echo $a |wc -c) -le $1 ] ; do
		a=${a}"$(( $RANDOM % 10 ))"
	done
	echo $a
}

getPass () {
	# $1 - lehth of password
	# return - random pass
	lenth=$1
	cat /dev/urandom | head -n 10 |md5sum | cut -c 1-$lenth
}

getHash() {
	# $1 - password
	# return - password hash and salt in /etc/shadow format
	python3 -c "import random,string,crypt;
randomsalt = ''.join(random.sample(string.ascii_letters,8));
print(crypt.crypt('"$1"', f'\$6\${randomsalt}\$'))"
}


check_union() {
	# return type of union fs
	#
	local UNION
	df / |grep -q ^aufs[[:space:]] >/dev/null 2>&1 && UNION='aufs'
	df / |grep -q ^overlay[[:space:]] >/dev/null 2>&1 && UNION='ovl'
	[ "$UNION" ]  || exitmsg "Can't determine the type of unionfs" 111
	echo "$UNION"
	}

make_union() { # hide from --help
	# Принимает параметры в формате ключ=значение
	# Любые из local, обязательный только LOWERDIRS
	local LOWERDIRS='' SPACE='' IMGFILE='' IMGMPOINT='' WORKDIR='' CHANGES='' UNION='' LINDEX=''
	local TMPDIR="$TMPUNION"
	local UNIONFS="$(check_union)"
	for a in $@ ; do
		local |grep -q "$(echo $a |cut  -f1 -d=)=" &&  eval "$a" 
	done
	mkdir -p ${TMPDIR}/0
	lastN=$(ls -1 ${TMPDIR} |sort -n |tail -n1)
	N=$(( $lastN + 1 ))	
	[ -z "$UNION" ] && UNION=${TMPDIR}/$N/UNION
 	[ -z "$IMGFILE" ] && IMGFILE=${TMPDIR}/$N/upper.img
	[ -z "$IMGMPOINT" ] && IMGMPOINT=${TMPDIR}/$N/upperMP
	[ -z "$WORKDIR" ] && WORKDIR=${TMPDIR}/$N/upperMP/workdir
	[ -z "$CHANGES" ] && CHANGES=${TMPDIR}/$N/upperMP/changes
	mkdir -p "$IMGMPOINT" "$UNION"
	[ -z "$SPACE" ] && SIZE=$(LC_ALL=C df -BM "${TMPDIR}" --output=avail | tail -1 | tr -d M)
	[ -z "$SPACE" ] && SPACE=$(( "$SIZE" * 80 / 100 ))
	dd if=/dev/zero of="$IMGFILE" bs=1 count=0 seek="$SPACE"M 1>&2 || return ${LINENO} 
	mkfs.ext2 "$IMGFILE" 1>&2 || return ${LINENO}
	mount -o loop "$IMGFILE" "$IMGMPOINT" 1>&2 || return ${LINENO}
	mkdir -p "$WORKDIR" "$CHANGES"
	if [ "$UNIONFS" = 'ovl' ] ; then 
		mount -t overlay overlay -o lowerdir="$LOWERDIRS",upperdir="$CHANGES",workdir="$WORKDIR" "$UNION" || return ${LINENO}
	elif [ "$UNIONFS" = 'aufs' ] ; then
		LOWERDIRS=$(echo $LOWERDIRS |sed 's#:#=ro+wh:#g')=ro+wh
		mount -t aufs -o nowarn_perm,br=${CHANGES}:${LOWERDIRS} aufs "$UNION"
	fi
	echo "UNION FS:" 1>&2 
	for a in INDEX=$N UNION=$UNION CHANGES=$CHANGES IMGMPOINT=$IMGMPOINT SPACE=$SPACE LINDEX=$LINDEX ; do
		echo $a
		echo ::::$a:::: 1>&2
	done
}

destroy_union() {  # hide from --help
	# UNION="точка монтирования overlayfs", IMGMPOINT=upperdir (если upperdir тоже точка монтирования) 
	# или $1 $2 диапазон номеров overlayfs созданных make-ovl 
	local UNION=''  IMGMPOINT='' STARTINDEX='' ENDINDEX=''
	for a in $@ ; do
		if local |grep -q "$(echo $a |cut  -f1 -d=)=" ; then
			eval $a
		else
			if [ -z $STARTINDEX ] ; then
				STARTINDEX=$a
				continue
			fi
			[ -z $ENDINDEX ] && ENDINDEX=$a
		fi 
	done
	
	destroy_by_index(){
		[ -z $ENDINDEX ] && ENDINDEX="$STARTINDEX"
		for a in $(seq $STARTINDEX $ENDINDEX) ; do
			destroy_by_path ${TMPUNION}/$a/UNION ${TMPUNION}/$a/upperMP
		done
	}
	destroy_by_path(){
		[ $3 ] && int=$3 || int=3
		[ "$int" -le  '0' ] && return 111
		[ ! -d $1 -a ! -d $2 ] && return 0
		key=''
		echo -n .
		[ "$int -eq 1" ] && key="-l"
		umount $key $1 2>/dev/null
		[ $2 ] && umount $key "$2" 2>/dev/nullrrr
		[ $1 != '/' ] && rm -rf $(dirname "$1")
		int=$(("$int" - 1))
		sleep 1
		destroy_by_path $1 $2 $int
	}
	expr $ENDINDEX + 1 >/dev/null 2>&1 && destroy_by_index
	[ -d "$UNION" ] && destroy_by_path "$UNION" "$IMGMPOINT"
	echo ""
}

# $1 - точка монтирования aufs 
# или номер aufs созданного make-ovl 
# $2 - спрятать rw слой (любое значение)
ls_aufs() { # hide from --help
	if [ "$1" ] ; then 
		expr "$1" + 1 >/dev/null 2>&1 && AUFS=${TMPUNION}/$1/UNION
		[ -d $1 ] && AUFS=$1
	else
		AUFS='/'
	fi
	# получаем значение для переменной si, присваивает eval
	eval $(grep -E '^aufs[[:space:]]+'${AUFS}'[[:space:]]+' /proc/mounts |sed 's/,/\n/g' |grep si=)
	if [ $2 ] ; then 
		cat  $(ls /sys/fs/aufs/si_$si/br* |sort -V) |grep ${SYSMNT} |sed 's/=.*$//' |sed 1d
	else
		cat  $(ls /sys/fs/aufs/si_$si/br* |sort -V) |grep ${SYSMNT} |sed 's/=.*$//'
	fi
}

ls_ovl() {  # hide from --help
	if [ "$1" ] ; then 
		expr "$1" + 1 >/dev/null 2>&1 && OVL=${TMPUNION}/$1/UNION
		[ -d $1 ] && OVL=$1
	else
		OVL=$SYSMNT
	fi
	getLayers() {
		cat /proc/mounts |grep "overlay.*$OVL" \
		|sed -e 's#^overlay.*,'$1'=\([^=]*\),.*#\1#' -e 's#:#\n#g'		
	}
	UPPERDIR=$(getLayers upperdir)
	LOWERDIRS=$(getLayers lowerdir)
	[ $2 ] || echo ${UPPERDIR}
	echo "${LOWERDIRS}" 
}

ls_union() {
	# $1 - aufs/overlayfs  mount point {default "/"}
	# return - list of layers
	UNIONFS=$(check_union)
	ls_$UNIONFS $@
	}

exitmsg() { # hide from --help
	# $1 - сообщение
	# $2 - exitcode
	# $3 - если есть $3 не завершать скрипт
	if [ "$2" -ne 0 ] ; then
		echo -ne "$color_red" ; echo -e "$1"  
		echo "exitcode: $2"
		echo -ne "$color_default"
		[ "$3" ] || exit $2 
	fi
} 

prepare_layers() { # hide from --help
	# MASK="шаблон в формате grep -E, для поиска слоев в основной overlayfs"
	# INDEX="номер unionfs, если создана make_union. По умолчанию - пусто"
	# LIST="список папок и файлов (squashfs, img c ext2/3/4)
	local a layer MASK='' LIST='' UNION='/' INDEX='' LINDEX='none'
	local TMPDIR=${TMPSQFS}
	for a in $@ ; do
		local |grep -q "$(echo $a |cut  -f1 -d=)=" &&  eval $a 
	done
	mkdir -p ${TMPDIR}/0
	lastN=$(ls -1 ${TMPDIR} |sort -n |tail -n1)
	N=$(( $lastN + 1 ))
	if [ -z "$INDEX" ] ; then 
		LSOVLPAR="$UNION"
	else
		LSOVLPAR="$INDEX"
	fi
	[ -z "$MASK" ] || layers=$(ls_union "$LSOVLPAR" |grep -E "$MASK")
	if ! [ -z $LIST ] ; then
		for layer in $(echo "$LIST" |sed 's#,# #g') ; do
		mpoint="$TMPDIR/$N/$(basename $layer)"
		mkdir -p $mpoint
		case $(file -b "$layer") in
			*directory) 
				mount --bind "$layer" "$mpoint" 2>/dev/null ;;
			Squashfs*4.0*|Linux*ext*)
				mount -o loop "$layer" "$mpoint" 2>/dev/null ;;
			*) return ${LINENO} ;;
		esac
		layers="$layers $mpoint"
	done
	LINDEX=$N
	fi
	[ -z "$MASK" -a -z "$LIST" ] && layers=$(ls_union "$LSOVLPAR")
	[ -z "$layers" ] && return ${LINENO}
	echo "LINDEX=$LINDEX LOWERDIRS=$(echo $layers |sed  -e 's# #:#g')"
	echo LAYERS: 1>&2
	for a in $layers ;do
	   echo :::$a::: 1>&2
	done
}

destroy_layers() {  # hide from --help
	# $1 или STARTINDEX=N начальный или единственный номер для уничтожения слоев
	# $2 или ENDINDEX=M конечный индекс
	local STARTINDEX='' ENDINDEX='' a n
	for a in $@ ; do
		if local |grep -q "$(echo $a |cut  -f1 -d=)=" ; then
			eval $a
		else
			if [ -z $STARTINDEX ] ; then
				STARTINDEX=$a
				continue
			fi
			[ -z $ENDINDEX ] && ENDINDEX=$a
		fi 
	done
	[ -z $ENDINDEX ] && ENDINDEX="$STARTINDEX"
	expr $1 + 1 >/dev/null 2>&1 || return ${LINENO}
	for n in $(seq $STARTINDEX $ENDINDEX) ; do
		[ -d ${TMPSQFS}/$n ]  || continue
		echo -n .
		for a in $(ls -1 ${TMPSQFS}/$n) ; do
			umount ${TMPSQFS}/$n/$a 2>/dev/null
		done
		[ -z "$(find ${TMPSQFS}/$n -type f)" ] && rm -rf ${TMPSQFS}/$n || return ${LINENO}
	done
	echo ''
}

clear() {
	# remove lib mount artifacts
	#
	local INDEX LINDEX
	LINDEX=$(ls -1 ${TMPSQFS}/ | wc -l)
	INDEX=$(ls -1 ${TMPUNION}/ | wc -l)
	destroy_union 1 $INDEX
	destroy_layers 1 $LINDEX
}

make_list() { # hide from --help
	local SOURCE='' DEST='' NAME='' SPEC='' DEPS=''
	for a in $@ ; do
		local |grep -q "$(echo $a |cut  -f1 -d=)=" &&  eval "$a" 
	done
	[ "$SOURCE" ] || return ${LINENO}
	source_root=$(realpath $SOURCE)
	dest_root="$source_root"
	[ "$DEST" ] && dest_root="$(realpath "$DEST")"
	pack_name="$(basename "${source_root%.$EXT}")"
	[ $NAME ] && pack_name=$NAME
	dest_dir="${dest_root}${METAINFODIR}/mount/${pack_name}/"
	mkdir -p $dest_dir	
if [ -d "${source_root}${METAINFODIR}/mount" -a $(ls -1 "${source_root}${METAINFODIR}/mount" 2>/dev/null |wc -l) -gt 1 ] ; then
	mkdir -p "${dest_dir}submod"
	cp  -fax "${source_root}${METAINFODIR}/mount"   /tmp/ 
	mv  -f /tmp/mount "${dest_dir}submod/" &&  rm -rf /tmp/mount
	for submod in $(ls -1 ${source_root}${METAINFODIR}/mount/ 2>/dev/null); do
		rm -rf ${dest_root}${METAINFODIR}/mount/$submod
	done
fi
[ ! -z $SPEC -a -f $SPEC ] && cp "$SPEC" ${dest_dir}/
find "${source_root}" ! -type d        | sed 's#'"$source_root"'##' |grep -E -v "$METAINFODIR" > "${dest_dir}/pfs.files"
find "${source_root}" -type d -empty   | sed 's#'"$source_root"'##' |grep -E -v "$METAINFODIR" > "${dest_dir}/pfs.dirs.empty"
[ "$DEPS" ] && echo "$DEPS" > "${dest_dir}/pfs.depends"
return 0  
}

root_only() { # hide from --help
	if [ $(id -un) != "root" ] ; then
		echo  "You must be root !!!"
		exit 1 
	fi
}

update_caches() {
	# update caches, icons, menus, etc after load/unload modules
	#
	[ "$(find ${1}/usr{,/local,/X11*}/{lib64,lib} -type f -name "*.so" 2>/dev/null)" ] && ldconfig #&
	[ -d "$1/usr/share/mime/" ] && update-mime-database /usr/share/mime &
	[ -d "$1/usr/lib/gdk-pixbuf-2.0/" ]  && gdk-pixbuf-query-loaders --update-cache &
	[ -d "$1/usr/share/icons/hicolor/" ] && gtk-update-icon-cache -f -i -q /usr/share/icons/hicolor &
	[ -d "$1/usr/share/glib-2.0/schemas/" ]  && glib-compile-schemas /usr/share/glib-2.0/schemas/ &
	if [ -d "$1/usr/share/applications/" -o -d "$1/usr/local/share/applications/" ] ; then 
		update-desktop-database -q &
		touch /usr/share/applications/screensavers &
	fi
	for font_dir in /usr/share/fonts{,/default}/TTF  /usr/X11R6/lib/X11/fonts/TTF ; do
		if [ -d "$1/${font_dir}" ]; then
			fcneed=on
			mkfontscale ${font_dir} & 
			mkfontdir ${font_dir} &
		fi
	done
	[ $fcneed ] && fc-cache -f -s &
}

clear_CHANGES() { # hide from --help
	rm -rf "$CHANGES/{dev,proc,sys,tmp,run}" >/dev/null
	rm -rf "$CHANGES"/var/tmp "$CHANGES"/var/cache/{dnf,ldconfig} "$CHANGES"/etc/ld.so.cache 2>/dev/null
	rm -rf "$CHANGES"/var/lib/menu "$CHANGES"/var/log "$CHANGES"/usr/share/applications/mimeinfo.cache 2>/dev/null
	find "$CHANGES"/usr/share/icons -type f -name icon-theme.cache 2>/dev/null | xargs rm -f
	rmdir "$CHANGES"/{dev,proc,sys,lost+found} "$CHANGES/var/cache" "$CHANGES/var/lib" "$CHANGES/var" "$CHANGES/etc" 2>/dev/null
	find "$CHANGES"  -name .wh..* | xargs rm -rf
	find "$CHANGES"  -xtype c -perm 0000 -size 0c | xargs rm -rf
}

# $1 файл (локальный, http, ftp, rsync)
# $2 файл для сохранения 
# вернет имя файла с путем
getfile() {
	# $1 - file {local, ftp, http, rsync}
	# $2 - file name to save 
	local file='/' 
	geturl() {
		if  echo "$1" | grep -E -q "^rsync://[a-z0-9./-]*" ; then
			eval rsync -avzz --progress "$1" "$2" 1>&2 $DEVNULL   || rm -f $2
		else
			eval wget -t 2 -O "$2"  "$1"  1>&2  $DEVNULL || rm -f $2
		fi
	}
	mkdir -p "$(dirname $2)"
	if echo "$1" | grep -E -q "^file://[a-z0-9./-]*" ; then
		file="$(echo $1 |sed 's#^file:/##')"
	elif echo "$1" | grep -E -q "^[a-z]{3,6}://[a-z0-9./-]*"; then
		geturl "$1" "$2"
	else
		file="$1"
	fi
	[ -f "$file" -a "$(realpath "$file")" != "$(realpath "$2")" ] && cp -a "$file" "$2"
	[ -f "$2" ] && echo "$2"
}

# $1 корень фс где искать файлы с метаинформацией от модулей
# $2,3,4,5... список файлов которые вывести
builders_info() {
	# info about dnf2mod, chroot2mod current builders
	#
	for builder in dnf2mod chroot2mod ; do
		if [ -d ${TMP}/var/lib/$builder ] ; then
			for package in $(ls -1 $1/var/lib/${builder}/) ; do
				echo ''
				for f in  $(ls -1 $1/var/lib/$builder/$package/) ; do
					echo "$@" |grep -qw "$f" || continue
					echo -en "$builder ${f} $package: "
					echo $(cat  $1/var/lib/$builder/${package}/$f)
				done
			done
		fi
	done
}

find_syspart() { # hide from --help
	grep -m1 " $SYSMNT/layer-base/0 " /proc/self/mountinfo | awk '{ print $10 }'
}

find_sysdir() { # hide from --help
    DISTRDEV=$(find_syspart)
    [ -e "$DISTRDEV" ] || return 1 
    DISTRMPOINT=$(findmnt -lnf $DISTRDEV -o TARGET)
    DISTRPATH=${DISTRMPOINT}$(grep -m1 " $SYSMNT/layer-base/0 " /proc/self/mountinfo | awk '{ print $4 }')
    [ -f "$DISTRPATH/vmlinuz" -a -d "$DISTRPATH/base" ] || return  1
    echo "$DISTRPATH"
}

remount() {
	# $1 - mount parameter
	# remount system dir {see find_sys} with $1 parameter
	par="$1"
	mount -o remount,$par $(find_syspart)
	mount -o remount,$par ${SYSMNT}/layer-base/0
}

if [ "$(basename $0)" = "lib" ] ;then
[ "$1" ] || HLP
[ "$1" == '--help' -o "$1" == '-h' ] && HLP
command="$1" 
shift
$command $@
exitmsg "$command  ERROR!!!" "$?"
fi



