#!/bin/sh
#library for pfs-utils (https://github.com/pfs-utils)
#Authors: puppyrus.org : Zay, DdShurick, sfs
#Author: magos-linux.ru: Betcher
#License: GPL v3
#VERSION 4.4

#help
HELP(){
echo "$0 is a helper library for Pfs-utils scripts" ; echo ""
echo "Usage:" 
echo "$0 <function> <pars>"
echo OR
echo "source $(realpath $0) ; <function> <pars>" ; echo ""
echo "Available functions:"
cat $0 |grep \(\).*{$
exit 1
}

for arg in $@
do
  case "${arg}" in
    "-h" | "--help") [ "`basename $0`" = "pfs" ] && { HELP ;exit 1 ; } ;;
  esac
done

PATH=${PATH}:/sbin
PFSDIR=/etc/packages
TMPFSLIMIT=100000
EXT=pfs

copyramdir="/tmp/.mountRAM"
prefixmp="/mnt/."
changesDir="/mnt/live/memory/changes/"
aufsroot="$(`which busybox 2>/dev/null` mount |egrep ' on / type aufs ')" 
[ "$aufsroot" ] && SYSMNT=/$(cut -f2 -d/ /sys/fs/aufs/si_$(grep ' / aufs' /proc/mounts | cut -f2 -d= | tr ',' ' ' | cut -f1 -d' ')/br0)
if [ -f /etc/initvars ] ; then
        . /etc/initvars
        copyramdir=${SYSMNT}/copy2ram/
        prefixmp=${SYSMNT}/bundles/
        changesDir=${SYSMNT}/changes/
fi

compression="-b 512K -comp xz"
compression_fast="-comp lz4"
if echo $compression |grep -q xz; then
	if uname -m | grep -q -e "86" -e "32" -e "64" ;then
	compression="$compression -Xbcj x86" 
	else 
	compression="$compression -Xbcj x86,arm"
	fi
fi

CFG_DIR="/etc/pfsget" 				# папка с конфигами для pfsget
PUBKEYS="$CFG_DIR/keys"				# папка для доверенных публтчных ключей
mirror_list="$CFG_DIR/mirror.lst"   # список зеркал 
ALIASCFG="$CFG_DIR/aliases.cfg"      # файл с алиасами для нечеткого поиска
WORK_DIR="/var/tmp/pfsget" 			# папка для хранения реполистов и проч. /var/lib/pfsget?

#user config
[ -f /etc/pfs.cfg ] && . /etc/pfs.cfg

usage () {
	echo -e "$(basename $0) is a library for pfs-utils \n" 
	echo "Usage:" 
	echo "	$(basename $0) <function> <pars>"
	echo "		OR"
	echo -e "	. $(realpath $0) ;  <function> <pars> \n"
	echo "functions list:"
	echo "$(cat $0 |grep '^.*\ *().*{\ *$' |sed 's/().*{//')"
	exit 1
}

COL(){
color_red='\033[0;31m'
color_green='\033[0;32m'
color_yellow='\033[0;33m'
color_blue='\033[0;34m'
color_default='\033[0m'
}

exitmsg() {
	if [ "$2" -ne 0 ] ; then
	echo -ne "$color_red" ; echo "$1" ; echo -ne "$color_default\n"
	[ "$3" ] || exit $2 
	fi
} 

allow_only_root() {
    if [ "0$UID" -ne 0 ]; then
        exitmsg "Only root can run $0" -1
    fi
}

trim () {
[ "$1" ] && r="$1" || return 1
[ -d "$r" ] || return 2
cat <<EOF | while read a ; do rm -f "$a" ; done
$r/var/log
$r/run
$r/etc/udev/hwdb.bin
$r/etc/ld.so.cache
$r/var/cache/ldconfig/aux-cache
$r/var/cache/fontconfig
$r/usr/local/share/applications/mimeinfo.cache
$r/usr/share/applications/mimeinfo.cache
EOF
find "$r/usr/share/icons" -type f -name icon-theme.cache 2>/dev/null | xargs rm -f
#$r/var/lib/chroot2pfs
#$r/var/cache/pacman/pkg
#$r/var/lib/pacman/sync
}

mklist () {
	local source_root dest_root pack_name spec_file depends
	while [ $1 ] ; do eval $1 ; shift ; done 
	[ "$source_root" ] || return 1
	source_root=$(realpath $source_root)
	if [ "$dest_root" ] ; then 
		dest_root="$(realpath "$dest_root")" 
	else 
		dest_root="$source_root"
	fi
	[ $pack_name ] || pack_name="$(basename "${source_root%.$EXT}")"
	dest_dir="${dest_root}${PFSDIR}/mount/${pack_name}/"
	mkdir -p $dest_dir	
if [ -d "${source_root}${PFSDIR}/mount" -a $(ls -1 "${source_root}${PFSDIR}/mount" 2>/dev/null |wc -l) -gt 1 ] ; then
  mkdir -p "${dest_dir}submod"
  cp  -fax "${source_root}${PFSDIR}/mount"   /tmp/ 
  mv  -f /tmp/mount "${dest_dir}submod/" &&  rm -rf /tmp/mount
  for submod in $(ls -1 ${source_root}${PFSDIR}/mount/ 2>/dev/null); do
	  rm -rf ${dest_root}${PFSDIR}/mount/$submod
  done
fi
[ ! -z $spec_file -a -f $spec_file ] && cp "$spec_file" ${dest_dir}/
find "${source_root}" ! -type d        | sed 's#'"$source_root"'##' |egrep -v '^/.wh..wh.|^'$PFSDIR'' > "${dest_dir}/pfs.files"
find "${source_root}"   -type d -empty | sed 's#'"$source_root"'##' |egrep -v '^/.wh..wh.|^'$PFSDIR'' > "${dest_dir}/pfs.dirs.empty"
[ "$depends" ] && echo "$depends" > "${dest_dir}/pfs.depends"
return 0 #иначе без $depends не нулевое завершение 
}

checkramfreeb () {
	local free  
if [[ -x $(which checkramfree) ]] ; then
	echo $(checkramfree) 
else
   free=$(df $copyramdir -B 1024 |tail -n1 |awk '{print $4}')
   freeram="$(free | grep 'Mem:' | tr -s ' ' | cut -f 4 -d ' ')"
   echo $free
fi 2>/dev/null
}

checksfsxzb () {
[ "$aufsroot" ] || return 1
grep squashfs /proc/filesystems || return 1
grep aufs /proc/filesystems     || return 1
#check xz
kerneluname="$(uname -r)"
kernel1ver="$(echo -n "${kerneluname}" | cut -f 1 -d '.' | cut -f 1 -d '-')"
if [ ${kernel1ver} -lt 3 ];then
  kernel2ver="$(echo -n "${kerneluname}" | cut -f 2 -d '.' | cut -f 1 -d '-')"
  if [ ${kernel2ver} -lt 6 ];then
    return 1
  else
    kernel3ver="$(echo -n "${kerneluname}" | cut -f 3 -d '.' | cut -f 1 -d '-')"
    if [ ${kernel3ver} -lt 38 ]; then
      return 1
    fi
  fi
fi
return 0
}

disktypeb () {
if disktype "$1" 2>/dev/null ; then 
	return 0
else	
	ftest=$(file "$1")
	if $(echo "$ftest" |egrep -qi cannot.*open) ; then
		echo "$ftest"
		return 1
	elif $(echo "$ftest" |egrep -qi block.*special) ;then
		echo $(blkid -s TYPE "$1")
		return 0
	else 
		echo "$ftest"
		return 0
	fi
fi
}

fs_type () {
diskinfo="$(disktypeb "$1" 2>/dev/null)"
if echo "${diskinfo}" | grep -qi -F "Ext2" ;then
  echo 'ext2'
elif echo "${diskinfo}" | grep -qi -F "Ext3" ;then
  echo 'ext3'
elif echo "${diskinfo}" | grep -qi -F "Ext4" ;then
  echo 'ext4'
elif echo "${diskinfo}" | grep -qi -F "squashfs" ;then
  echo 'squashfs'
elif echo "${diskinfo}" | grep  -qi "ISO.*9660" ;then
  echo 'iso9660'
fi
}

lddcheck () {
	local rootdir alllibs
[ "${1}" != "" ] && rootdir="${1}" || rootdir="./"
if [ ! -d "${rootdir}" ]; then
  echo "Directory \"${rootdir}\" not found!" >&2; exit 1
fi
alllibs="$(find "${rootdir}" ! -type d -name "*.so*")" 
find "${rootdir}" -type f -executable | while read chfile; do [ "$(file --brief "${chfile}" | grep -E "LSB executable|shared object")" ] && ldd "${chfile}" 2>/dev/null; done | grep -E '\.so$|\.so\.[0-9]' | sed 's/(..........)/()/' | sort -u | sed 's/[ \t]*//;s/.* =>  ()//;s/ => .*$//;s/ ()//' | while read exlib; do echo "${alllibs}" | grep -q -F "${exlib}" || echo "${exlib}"; done | grep -v -F "linux-gate.so" | sort -uf
return 0
}

losetupb() { losetup "$@" || losetup-FULL "$@"   ; }
mountb()   { mount "$@" || busybox mount "$@"  ; }
umountb()  { umount "$@" || busybox umount "$@"  ; }

mkaufs () {
#make new aufs
N=1
while [ -f $SYSMNT/aufs${N}.lock ] ; do  N=$(($N +1)) ; done 
echo "$SYSMNT/aufs$N" > $SYSMNT/aufs${N}.lock
mkdir $SYSMNT/changes$N
mount -t tmpfs tmpfs /$SYSMNT/changes$N
mkdir $SYSMNT/aufs$N
mount -t aufs -o dirs=/$SYSMNT/changes$N/=rw aufs /$SYSMNT/aufs$N && echo "$SYSMNT/aufs$N" || return 1
}

delaufs () {
#remove aufs from $1 to $2
[ "$1" ] || return 2
End="$1" ; [ "$2" ] && End="$2"
for N in $( seq $1 $End ) ; do
	for tm in {sys,proc,dev}; do
		while grep $SYSMNT/aufs$N/$tm /proc/mounts; do
			echo $SYSMNT/aufs$N/$tm
			umount $SYSMNT/aufs$N/$tm
		done
	done
umount $SYSMNT/aufs$N 2>/dev/null
rmdir $SYSMNT/aufs$N 2>/dev/null
if [ "$(grep changes$N /proc/mounts)" ]; then
	umount -d $SYSMNT/changes$N &&	rmdir $SYSMNT/changes$N
else
	rm -rf $SYSMNT/changes$N
fi 2>/dev/null
if [ -d $SYSMNT/bundles$N ]; then
	ls -1A $SYSMNT/bundles$N | while read D
	do
		umount -d $SYSMNT/bundles$N/"$D" && 	rmdir $SYSMNT/bundles$N/"$D"
	done
	 rmdir $SYSMNT/bundles$N
fi 2>/dev/null
if [ "$(grep tmpfs$N /proc/mounts)" ]; then
	umount $SYSMNT/tmpfs$N && 	rmdir $SYSMNT/tmpfs$N
else
	rm -rf  $SYSMNT/tmpfs$N
fi 2>/dev/null
ls -1 $SYSMNT | egrep  "aufs${N}$|changes${N}$|bundles${N}$" && delaufs_error=yes
rm -f $SYSMNT/aufs${N}.lock
done
[ $delaufs_error ] && return 3
return 0
}

addlayer () {
#add new aufs layer, $1 - aufs number, $2 - layer source
AUFSMNT="/"
case "$1" in
	0) shift;;
	[1-9]) N=$1
		if [ $N -lt $(ls -d /sys/fs/aufs/si_* | wc -w) ]; then
			AUFSMNT=$SYSMNT/aufs$N
			shift
		else
			return 2
		fi
	;;
	*) return 3
esac
NEWLAYER="$(realpath "$(echo $@ )" )" || return 1
[ "$(grep aufs$N /proc/mounts)" ] || return 4
MODNAME="$(basename "$NEWLAYER" |sed -e 's/,/_comma_/g' -e 's/=/_equal_/g' -e 's/\*/_asterisk_/g' -e 's/\#/_hash_/g')"
#if df --output=fstype $NEWLAYER |grep -q ^aufs$ ;then
if [ "$(cat /proc/mounts |egrep '^'`df "$NEWLAYER" |tail -1 |cut -f1 -d " "`' ' |cut -f3 -d " " |uniq)" = aufs ];then
	[ "$(grep $SYSMNT/tmpfs$N /proc/mounts)" ] || ( mkdir $SYSMNT/tmpfs$N && mount -t tmpfs tmpfs $SYSMNT/tmpfs$N )
	cp -auPR "$NEWLAYER" "$SYSMNT/tmpfs$N/"
	NEWLAYER="${SYSMNT}/tmpfs${N}/$(basename "$NEWLAYER")"
fi
case $(file -b "$NEWLAYER") in
	*directory) 
		mkdir -p "$SYSMNT/bundles$N/$MODNAME"
		mount --bind "$NEWLAYER" "/$SYSMNT/bundles$N/$MODNAME"
		NEWLAYER="$SYSMNT/bundles$N/$MODNAME"
	;;
	Squashfs*4.0*|Linux*ext*)
		mkdir -p "$SYSMNT/bundles$N/$MODNAME"
		mount -o loop "$NEWLAYER" "/$SYSMNT/bundles$N/$MODNAME"
		NEWLAYER="$SYSMNT/bundles$N/$MODNAME"
	;;
	*) return 5 ;;
esac
mount -o remount,add:1:"$NEWLAYER"=ro+wh "$AUFSMNT" || return 5
echo "$NEWLAYER"
}

mksqmod(){
#make module
excludes=""
[ -f /tmp/mkpfs_excludes ] && excludes="-ef /tmp/mkpfs_excludes" 
eval mksquashfs "${1}" \""${2}"\" ${compression} ${excludes} -noappend ${noprogress} ${useproc} -wildcards $wh $devnull
exitmsg "error create squashfs module" $? $3
rm /tmp/mkpfs_excludes 2>/dev/null
return 0
}

pfsramfree () {
#rm unmounted modules and empty dirs from $copyramdir 
if [ "$1" ]; then
	[ -f "$1" ] && fsname="`basename $(realpath "$1")`" || fsname="$(basename "$1")"
	fullname=$(echo "${copyramdir}/${fsname}" |sed 's://:/:')
	[ -f "$fullname" ] &&  rm -f "$fullname" || return 1
else
	if [ -d "${copyramdir}" ]; then
    find "${copyramdir}" -mindepth 1 -type f 2>/dev/null | while read rampfs ; do
		losetupb -a | grep -q "${rampfs}" || rm -f "$rampfs"
    done
    find "${copyramdir}" -mindepth 1 -type d -empty 2>/dev/null | while read empdir ; do
		rmdir --parents $empdir 2>/dev/null
	done
	fi
fi
}

pfs_update_caches () {
#update caches, icons, menus, etc after load/unload modules
[ "$(find "$1"{,/usr{,/local,/X11R6}}/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 &
}

checkdeps () {
# $1 module mount point
list=$(find ${1}${PFSDIR}/mount/ -name pfs.depends -exec cat "{}" \; |sort |uniq)
founded=$(echo -e "$list\n$(pfsinfo |sort |uniq)" |sort |uniq -d)
echo -e "$founded\n$list" |sort |uniq -u
}

lsblocked () {
LSOF=$( lsof   | awk '{print $9}' |grep "\/..*" | egrep -v "/proc|/sys|/run|/tmp|/dev|/home" | sort -u)
for a in $LSOF ; do
[ -f $a ] || continue
[ -f $changesDir/$a ] && continue
blockFiles="$blockFiles\n$a"
done

for bundle in $(aufs-n --raw '$bundle') ; do
if [[ -n $(echo -e "${blockFiles}\n$(find $bundle | sed "s:$bundle::")" |sort |uniq -d) ]] ; then 
echo "$bundle"
fi
done
}


if [ "$(basename $0)" = "pfs" ] ;then
[ $1 ] || HELP
command="$1" 
shift
$command "$@"
exitmsg "$command  ERROR!!!" "$?"
fi 
