#!/bin/bash
# barium helper scripts
# author: rosalinux.ru: betcher_
. /etc/initvars 
CURPWD="$(pwd)"

export TEXTDOMAINDIR=/usr/share/locale
export TEXTDOMAIN=rosa-rw_functions
MSG01="$(gettext -s "Update process started. Please wait")"
MSG02="$(gettext -s "Update finished")"
MSG03="$(gettext -s "Please reboot")"
MSG04="$(gettext -s "Update system")"
MSG05="$(gettext -s "Check updates")"
MSG06="$(gettext -s "Aborted by user")"
MSG07="$(gettext -s "OS has not been restarted since the previous update")"
MSG08="$(gettext -s "Updates are not found")"
MSG09="$(gettext -s "Download failed")"


function  start_progress () {
	rm -f /tmp/progress-pipe
	mkfifo /tmp/progress-pipe
	( while true ; do
		read a < /tmp/progress-pipe
		echo "$a"
	done | xuserrun zenity --progress \
  --title="$MSG04" \
  --text="$MSG05" \
  --auto-close \
  --no-cancel \
  --percentage=0 
  rm -f /tmp/progress-pipe ) & 
  ppid=$!
}

function checkfreemedia() {
if [ -f ${SYSMNT}/layer-base/0/VERSION ] ; then
	echo "Freemedia mode disabled, system media already mountеd"
	return 0
else
	echo "Freemedia mode enabled. Try -mm (mount media key)"
	return 1 
fi 
}

function mountmedia () {
    for script in $(ls /run/bin/remount_*) ; do
	$script >/dev/null
    done
    if [ -f ${SYSMNT}/layer-base/0/VERSION ] ; then
		echolog "System media mounted successfully"
		NEEDUMOUNT=yes
		return 0
    fi
    echolog "System media is not mounted and cannot be mounted automatically"
    return 1
}

function echolog() {
 echo "$(basename $0): $(date +"%Y-%m-%d %H:%M:%S") $@" >>$LOGFILE
 echo "$(basename $0): $(date +"%H:%M:%S") $@" >>/dev/stdout
 if [ "$2" ] ; then 
	(echo "$2" > /tmp/progress-pipe
	sleep 1
	echo "# $1" > /tmp/progress-pipe) &
 fi
}

function echoerror() {
 echolog $@
 echowall "$@"
 echo "100" > /tmp/progress-pipe
 resetMounts 
 exit 1
}

function echowall() {
 notify-send "barium update: $@" || mdialog --passivepopup "barium update: $@"
}

function findsystemdir() {
    DISTRDEV=$(grep -m1 " $SYSMNT/layer-base/0 " /proc/self/mountinfo | awk '{ print $10 }')
    [ -e "$DISTRDEV" ] || checkfreemedia || exit 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
    DISTRNAME=$(cat ${DISTRPATH}/VERSION |awk '{print $1}')
    DISTRVER=$(cat ${DISTRPATH}/VERSION |awk '{print $2}')
    [ -z "$DISTRNAME" -o -z "$DISTRVER" ] && return  1
}

function initparameters() {
    if [ "$(free -m |sed -n '/Mem:/s/^.*\ //p')" -gt 1000 ] ; then
	MUPDRSYNCOPT=" -ah --progress --stats "
    else
    	MUPDRSYNCOPT=" --inplace -ah --progress --stats "
    fi
    UPDATEPROT=rsync
    UPDATESERVER=mirror.yandex.ru
    UPDATESHARE=/rosa/repository
    . /usr/lib/rosa-rw/os-config
    . /etc/ROSA-RW/config
    findsystemdir
    UPDATEURL=$UPDATEPROT://$UPDATESERVER/$UPDATESHARE/$UPDATEDIR/ROSA-SYSTEM/
    UPDATEMODSURL=$UPDATEPROT://$UPDATESERVER/$UPDATESHARE/$UPDATEDIR/modules/
    UPDATEINTURL=$UPDATEPROT://$UPDATESERVER/$UPDATESHARE/$UPDATEDIR/i18/
}

# $1 filename
# $2 directory to save
function download() {
 case $UPDATEPROT in
    rsync)
        echolog "${LINENO}: Downloading $UPDATEURL$1 to $2"
        rsync $MUPDRSYNCOPT "$UPDATEURL$1" "$2"  && return 0
        ;;
    *)
        echoerror "${LINENO}: Protocol $UPDATEPROT is not supported currently, sorry"
 esac
 echoerror "$MSG09"
}

# $1 filename
# $2 directory to save
function cdownload() {
 [ -f "$2/$1" ] && return 0
 download "$1" "$2" || return 1
}

# $1 filelist (MD5SUM)
# $2 directory to save
# $3 filter
function downloadfrom_repolist() {
    N=$(cat $1 |wc -l)
    step=$((60 / $N))
    percent=20
    echolog "Download files from $1 list" "$(( "$percent" + 2 ))"
    for a in $(gawk '{print $1}' $1) ;do
	percent=$(($percent + $step))
	echolog "processing:  $a" $percent
	echo $a | grep -q "$3" || continue
	name2save=$(realpath $2)/"$a"
	path2save=$(dirname $name2save)
	oldfile=${DISTRPATH}/"$a"
	[ -f $oldfile ] && rsync -ah --progress $oldfile ${path2save}/ 
	download $a $path2save
    done
}

function checkserver() {
 echolog "Checking connection to server ..."
 download VERSION    	"$TMPDIR"
 download _REPOLIST     "$TMPDIR"
 download _REPOLIST.sig "$TMPDIR"
 echolog "Connection to server is ok"
}

# $1 original _REPOLIST
# $2 new _REPOLIST
# $3 _REPOLIST with updates
function compare_repolists() {
 rm -f "$3" 2>/dev/null
	cat $1 $2 |sed 's/ .*MD5=/ /' |sort |uniq -u | cat - $2 |sed 's/ .*MD5=/ /' |sort |uniq -d > $3
 [ -n "$(cat $3)" ] || return 1
}


# $1 _REPOLIST file
# $2 directory with files
function checkfilesums() {
local  checked MD5_FILE MD5_REPOLIST
 [ -f "$1" ] || echoerror "$1 not exists"
 echolog "${LINENO}: Checking MD5 sums for files..."
 checked=ok	
 for a in $(find $2 -type f) ;do
    MD5_FILE=$(md5sum "$a" |cut -f1 -d ' ' |xargs)
    MD5_REPOLIST=$(cat $1 |grep -m1 $(echo $a |sed "s:^$2::") |sed 's/.*MD5=//' |xargs)
	if [ -z "${MD5_REPOLIST}" ] ; then
		echo "${LINENO}: $a is not in $1"
	elif [ "${MD5_FILE}" != "${MD5_REPOLIST}"  ];then
        echo "${LINENO}: md5sums not equal $a"
		checked=error
    fi
 done	
	if [ "$checked" != 'ok' ] ; then
		echo "Something went wrong, as you see. Continue (Y/N)?"		
		read aaa  
		[ "$aaa" != 'Y' -a "$aaa" != 'y' ] && echoerror "Aborted..."
	else
 		echolog "Checking complete."
	fi
}

# $1 signature
# $2 file to check
function checksign() {
  if gpg --verify --keyring /usr/share/rosa-rw/pubkeys/main.gpg "$1" "$2" ;then
	echolog "File $(basename $2) signed by /usr/share/rosa-rw/pubkeys/main.gpg"
	return 0
  fi
 echoerror "Incorrect signature in $(basename $1)"
}

function checknewversion() {
 echolog "Checking for new version ..."
 cdownload VERSION    "$TMPDIR"
 cdownload _REPOLIST     "$TMPDIR"
 cdownload _REPOLIST.sig "$TMPDIR"
 checksign "$TMPDIR/_REPOLIST.sig" "$TMPDIR/_REPOLIST" || exit 1
 CURVER=$(gawk '{print $2}' "${DISTRPATH}/VERSION" )
 NEWVER=$(gawk '{print $2}' "$TMPDIR/VERSION")
 # may be float
 if awk 'BEGIN {return_code=('$NEWVER' <= '$CURVER') ? 0 : 1; exit} END {exit return_code}' ; then
    echolog "New version not found."
    echolog "server --> $NEWVER, current --> $CURVER"
    return 1
 fi
 echolog "Found new version $NEWVER"
}

function checkupdates() {
 echolog "Checking for updates ..."
 cdownload VERSION    "$TMPDIR"
 cdownload _REPOLIST     "$TMPDIR"
 cdownload _REPOLIST.sig "$TMPDIR"
 checksign "$TMPDIR/_REPOLIST.sig" "$TMPDIR/_REPOLIST" || exit 1
 CURVER=$(cat ${DISTRPATH}/VERSION |awk '{print $2}')
 NEWVER=$(gawk '{print $2}' "$TMPDIR/VERSION")
 if [ "0$NEWVER" != "0$CURVER" ] ;then
    echolog "Current version doesn't match server version"
    return 1
 fi
 if ! compare_repolists "$DISTRPATH/_REPOLIST" "$TMPDIR/_REPOLIST" "$TMPDIR/_UPDATES" ;then
    echolog "Updates are not found."
    return 1
 fi
 cat "$TMPDIR/_UPDATES" | while read a ;do
    echolog "Found updated file: $a"
 done
}

function checkrootfs() {
 if grep $DISTRMPOINT  /proc/mounts | awk '{print "," $4 ","}' | grep -q ,ro, ;then
    echolog "Checkrootfs: root filesystem is mounted as readonly."
    NEEDREMOUNT=yes
    return 1
 fi
}

function remountRw () {
	mount -o remount,rw "$DISTRMPOINT"
	echolog "remountRw: FS $DISTRMPOINT remounted RW"
}

function resetMounts() {
	[ "$NEEDREMOUNT" ] && mount -o remount,ro "$DISTRMPOINT"
	[ "$NEEDUMOUNT" ] && grep $DISTRDEV /proc/mounts |tac |cut f2 -d ' ' |while read mpoint ; do
	umount $mpoint ; done
	cd "$CURPWD"
	echo "100" > /tmp/progress-pipe
	kill $ppid 2>/dev/null
	rm -f /tmp/progress-pipe 2>/dev/null
}

function checkneedroot() {
 if [ -w "$DISTRPATH" ] ;then
    echolog "Checking user acceess: ok."
 else
    echolog "Checking user acceess: failed."
    return 1
 fi
}

# $1 optional parameter
function restartasroot() {
 if [ "$UID" != "0" ] ;then
    echolog "Restarting script as root."
    bashsu "$(readlink -f $0) $@" || echowall "Authentification failed."
    exit $?
 fi
}

function startwall() {
 echolog "$MSG01 : $(basename $0) : $UID"
 echowall "$MSG01"
}

function endwall() {
 if grep -qi "please[[:space:]]*reboot" "$LOGFILE" ;then
    echowall "${MSG02}. $MSG03"
 else
    echowall "$MSG02"
 fi
}

# $1 modulename
function ismodactive() {
  aufs-n --raw '$bname_source' | grep -q $(basename "$1") || return 1
}

# $1 modulename
function deletemodule(){
	if [ -d $SYSMNT/ovl/changes ] ; then
		echolog "Overlayfs allows to deactivate module $(basename $1), please reboot."
		mv -f "$1" "$TMPDIR"
		return 1
	fi
	if ismodactive "$1" ;then
		if ! pfsunload $(basename "$1") ;then
			echolog "Can't deactivate module $(basename $1), please reboot."
			mv -f "$1" "$TMPDIR"
		return 1
    fi
	fi
 rm -f "$1"
}

# $1 full path to MD5SUM
# $2 path to base directory
function deleteobsolete() {
 pushd "$2" >/dev/null
 echolog "Removing obsolete modules..."
 for a in * ;do
    grep -q "/$a" "$1"  && continue
    #echo $a | egrep -qi "local|[*]" && continue
    if [ "$DEACTIVATE" = "yes" ] ;then
        deletemodule "$a"
    else
        rm -f "$a"
    fi
    echolog "removed obsolete file $a"
 done
 popd >/dev/null
}

function movedisabled() {
 echolog "Moving disabled base modules to optional..." 92
 cd base
 for a in *.xzm ;do
    [ -f "$DISTRPATH/optional/$a" -a ! -f "$DISTRPATH/base/$a" ] || continue
    [ -f "$a" ] && mv -f "$a" ../optional && echolog "Moved module $a to optional"
 done
 cd ..
}

function moveupdates() {
 echolog "Moving updates to system directory..." 95
 find ./ -type f | grep -v _REPOLIST | sed s=^./== | while read a ;do
    echolog "Moving file $a"
    ismodactive "$a" && deletemodule $DISTRPATH/$a
    if [ ! -f "$DISTRPATH/$a" ] ;then
	mv -f "$a" "$DISTRPATH/$a"
		[ -d $SYSMNT/ovl/changes ] || pfsload "$DISTRPATH/$a"
    else
		mv -f "$a" "$DISTRPATH/$a"
    fi
 done
 echolog "Moving complete."
}

function updateversion()
{
 [ -f /run/barium_updated ] && echoerror "$MSG07" 
 startwall
 echolog "Preparing work directory ${DISTRPATH}.upd ..." 12
 if ! [ -d "${DISTRPATH}.upd" ] ; then
	[ -d "${DISTRPATH}.bak" ] &&  mv  "${DISTRPATH}.bak" "${DISTRPATH}.upd"
	mkdir -p "${DISTRPATH}.upd" 2>/dev/null
 fi
 echo "15" > /tmp/progress-pipe
 rsync -ahx --delete "${DISTRPATH}/" "${DISTRPATH}.upd"
 cd "${DISTRPATH}.upd" || echoerror "Can't create directory ${DISTRPATH}.upd"
 mv -f $TMPDIR/VERSION VERSION
 cp -pfr -t ./ "$DISTRPATH/"*.sgn "$DISTRPATH/"*.ini "$DISTRPATH/modules" "$DISTRPATH/optional" "$DISTRPATH/base"
 echolog "Destination directory is ready now."
 echolog "Downloading files..." 20
 download _REPOLIST	./	
 download _REPOLIST.sig	./
 checksign _REPOLIST.sig _REPOLIST
 downloadfrom_repolist _REPOLIST ./ '.'
 echolog "Downloading finished." 90
 
 checkfilesums "_REPOLIST" "./"

 movedisabled

 DEACTIVATE=no deleteobsolete "$PWD/_REPOLIST" "$PWD/base"
 echolog "Renaming livemedia folders..." 95
 sync
 echo "95" > /tmp/progress-pipe
 mv "${DISTRPATH}" "${DISTRPATH}.bak" || exit 1
 mv "${DISTRPATH}.upd" "${DISTRPATH}"
 echolog "Update finished. Please reboot." 100
 cat $LOGFILE >> "${DISTRPATH}.bak/update.log"
 : > /run/barium_updated 
 endwall
}

function updatebase()
{
 [ -f /run/barium_updated ] && echoerror "$MSG07" 
 startwall

 echolog "Preparing directory ${DISTRPATH}.upd" 12
 mkdir -p "${DISTRPATH}.upd"/base
 cd "${DISTRPATH}.upd" || echoerror "Can't create directory ${DISTRPATH}.upd"
 compare_repolists "$DISTRPATH/_REPOLIST" "$TMPDIR/_REPOLIST" "$TMPDIR/_UPDATES"
 echolog "Destination directory is ready now." 20

 echolog "Downloading updates..." 21
 downloadfrom_repolist $TMPDIR/_UPDATES ./  base || exit 1
 #download _REPOLIST
 
 echolog "Downloading finished." 90
 checkfilesums "$TMPDIR/_REPOLIST" "./"
 movedisabled
 moveupdates
 DEACTIVATE=yes deleteobsolete "$TMPDIR/_REPOLIST" "$DISTRPATH/base"
 cp -pfr -t "$DISTRPATH" "$TMPDIR/_REPOLIST" "$TMPDIR/_REPOLIST.sig"
 rm -fr "${DISTRPATH}.upd"
 echolog "Update finished." 100
 : > /run/barium_updated
 endwall
}

function help()
{
 echo "This script updates system from server"
 echo "Usage: $(basename $0) [parameter]"
 echo "Parameters:"
 echo " -h|--help		this help"
 echo " --test              download files only (must be first parameter - $1)"
 echo " -cd|--checkdir		shows system directory and check FS for readonly"
 echo " -cr|--checkroot	checks if root access is required"
 echo " -cf|--checkfreemedia    checks fremedia, and trying to remount system media"   
 echo " -cv|--checknewversion	checks for new version is available"
 echo " -cu|--checkupdates	checks for new updated modules are available"
 echo " -cs|--checkserver	checks connection to server"
 echo " -uv|--updateversion	updates system release if it's available"
 echo " -ub|--updatebase	updates system modules if they are was updated on server"
 echo "Script without parameters does all checks and tryes to update system if it's necessary."
}


if [ "$1" == "--test" ] ; then
	TESTONLY=yes
	shift
fi

TMPDIR="/tmp/update-$UID"
LOGFILE=/tmp/update-$UID.log
rm -fr "$TMPDIR" 2>/dev/null
mkdir -p "$TMPDIR"
touch $LOGFILE
chmod 666 $LOGFILE

trap 'resetMounts; echolog "$MSG06" ' 2 3

case $1 in
    -cd|--checkdir)
	initparameters 
	echolog "$DISTRNAME directory found at $DISTRPATH"
	checkrootfs && exit 0 || exit 1
    ;;
    -cr|--checkroot)
	initparameters
	checkneedroot && exit 0 || exit 1
    ;;
    -cf|--checkfreemedia)
	checkfreemedia && exit 0 || exit 1
    ;;
    -mm|--mountmedia)
	mountmedia && exit 0 || exit 1
    ;;
    -cv|--checknewversion)
	initparameters
	checknewversion && exit 0 || exit 1
    ;;
    -cu|--checkupdates)
	initparameters
	checkupdates && exit 0 || exit 1
    ;;
    -cs|--checkserver)
	initparameters
	checkserver && exit 0 || exit 1
    ;;
    -uv|--updateversion)
	restartasroot -uv
	checkfreemedia || mountmedia
	initparameters
	checkrootfs || remountRw
	if checknewversion ;then
	    updateversion && exit 0 || exit 1
	fi
    ;;
    -ub|--updatebase)
	restartasroot -ub
	checkfreemedia || mountmedia
	initparameters 
	checkrootfs || remountRw
	if checkupdates ;then
	    updatebase && exit 0 || exit 1
	fi
    ;;
    ""|--auto)
    restartasroot --auto
	start_progress
	checkfreemedia || mountmedia
	echo "5" > /tmp/progress-pipe
	initparameters
	checkrootfs || remountRw
	checkserver || echoerror "server did not respond correctly" 100
	
	if checknewversion ;then
		echolog "Starting: update version mode" 10
	    updateversion
	elif checkupdates ;then
	    echolog "Starting: update modules mode" 10
	    updatebase
	else
		echowall "$MSG08"
	fi
	;;
    *)
        help
    ;;
esac

resetMounts

exit 0

