#############################################################################
# @package		LayerPIE
# @description	Шаблон задачи: Обновление ПО
# @copyright	2018-2025 Applite (https://applte.ru)
# 
# @license		commercial (https://layerpie.ru/licence.pdf)
#############################################################################


#######################################################################################
## Параметры из системы управления
#######################################################################################
NAME='%SYSTEM_LAYER_HOST_ROLE%'
LIST='%SYSTEM_LAYER_HOST_CONFIG%'
USERS='%SYSTEM_LAYER_LOCAL_USERS%'
ISBORG=%SYSTEM_BORG_ENABLED%
LAYER_ROOT=%SYSTEM_LAYER_ROOT%
# Системная переменная - количество доступных конфигураций на хосте 
# BACKUP_LEVEL=%SYSTEM_LAYER_HOST_BACKUP_LEVEL% 

#######################################################################################
## Установка темы оформления GRUB
#######################################################################################
echo %SYSTEM_GRUB_HEAD% | base64 --decode | sed -e "s/\r//g" >/boot/grub/grub.head
echo %SYSTEM_GRUB_FOOTER% | base64 --decode | sed -e "s/\r//g" >/boot/grub/grub.footer






#######################################################################################
## Установка скрипта для хука в initramFS
#######################################################################################
echo %SYSTEM_INITRD_HOOK% | base64 --decode | sed -e "s/\r//g" >/usr/share/initramfs-tools/hooks/prometheus
chmod +x /usr/share/initramfs-tools/hooks/prometheus






#######################################################################################
## Установка скрипта для выполнения в initramFS c слоя RW
#######################################################################################
echo %SYSTEM_INITRD_LAYER% | base64 --decode | sed -e "s/\r//g" >/Layer/bin/layer
chmod +x /Layer/bin/layer


#######################################################################################
## Поиск и монтирование USB-носителя с хранилищем слоев
#######################################################################################
echo
echo "Поиск и монтирование USB-носителя c переносным репозиторием слоев"
mkdir -p /tmp/repo
cmd="mount -L USBREPO /tmp/repo"
$cmd 2>/dev/null
isusb=$?
if [ $isusb -eq 0 ]; then
 echo "- USB обранужен и примонтирован"
else
 echo "- USB не обранужен"
 rm -r /tmp/repo
fi
echo







#######################################################################################
## Функция проверки наличия слоя на хосте, в локальном хранилище, на сервере репликации
#######################################################################################
function LayerCheck {
 FILE=$LAYER_ROOT/host/$LAYER_PATH/$LAYER_FILENAME
 if [ ! -f $FILE ]; then
 if [ $isusb -eq 0 ]; then
 echo -n " поиск файла в переносном репозитории "
 if [ -f /tmp/repo/$LAYER_PATH/$LAYER_FILENAME ]; then
 echo -n " файл найден "
 mkdir -p $LAYER_ROOT/host/$LAYER_PATH
 cp /tmp/repo/$LAYER_PATH/$LAYER_FILENAME $FILE
 fi
 fi
 if [ ! -f $FILE ]; then
    echo -n " загрузка с сервера "
    if [ "${ISBORG}" = "1" ]; then
        echo "Загрузка через borg"
        RSYNC_PASSWORD=mirror ${LAYER_ROOT}/bin/rsync -avz -H --stats --port 4444 --include="/${LAYER_PATH}@${LAYER_FILENAME}/" --include="/${LAYER_PATH}@${LAYER_FILENAME}/${LAYER_FILENAME}" --exclude=* mirror@%SYSTEM_LAYER_SERVER_ADDRESS%::data ${LAYER_ROOT}/host/
        echo RSYNC_PASSWORD=mirror ${LAYER_ROOT}/bin/rsync -avz -H --stats --port 4444 --include="/${LAYER_PATH}@${LAYER_FILENAME}/" --include="/${LAYER_PATH}@${LAYER_FILENAME}/${LAYER_FILENAME}" --exclude=* mirror@%SYSTEM_LAYER_SERVER_ADDRESS%::data ${LAYER_ROOT}/host/
        mkdir -p ${LAYER_ROOT}/host/${LAYER_PATH}
        mv ${LAYER_ROOT}/host/${LAYER_PATH}@${LAYER_FILENAME}/${LAYER_FILENAME} ${LAYER_ROOT}/host/${LAYER_PATH}/${LAYER_FILENAME}
        rm -R ${LAYER_ROOT}/host/${LAYER_PATH}@${LAYER_FILENAME}
    else
        echo "Загрузка без borg"
        RSYNC_PASSWORD=mirror $LAYER_ROOT/bin/rsync -avz -H --stats --port 4444 --include="/$LAYER_PATH/" --include=""/$LAYER_PATH/$LAYER_FILENAME"" --exclude=* mirror@%SYSTEM_LAYER_SERVER_ADDRESS%::mirror $LAYER_ROOT/host/ >/dev/null
    fi
 fi

 if [ ! -f $FILE ]; then
 FILE_CRC=NULL
 else
 FILE_CRC="$( $LAYER_ROOT/bin/crc32 $FILE )"
 fi
 else
 FILE_CRC="$( $LAYER_ROOT/bin/crc32 $FILE )"
 fi
}








#######################################################################################
## Загрузка слоев из новой нонфигурации
#######################################################################################
KERNEL_FILE="NULL"
KERNEL_PATH="NULL"
echo "Загрузка слоев из конфигурации"
for row in $(echo "${LIST}" | $LAYER_ROOT/bin/jq -r '.[] | @base64'); do
 _jq() {
 echo ${row} | base64 --decode | $LAYER_ROOT/bin/jq -r ${1}
 }
 LAYER_PATH=$(_jq '.path')
 LAYER_FILENAME=$(_jq '.filename')
 LAYER_CRC=$(_jq '.crc')
 LAYER_ISKERNEL=$(_jq '.is_kernel')

 [ "$LAYER_ISKERNEL" = "null" ] && LAYER_ISKERNEL=0
 if [ $LAYER_ISKERNEL -eq 1 ]; then
 KERNEL_FILE=$LAYER_FILENAME
 KERNEL_PATH=$LAYER_PATH
 fi
 echo -n "- Проверка слоя "$LAYER_FILENAME" (требуемый crc="$LAYER_CRC") "
 while : ; do
 LayerCheck
 if [[ "$LAYER_CRC" == "$FILE_CRC" ]]; then
 echo " (текущий crc=$FILE_CRC) OK"
 break;
 else
 echo " (текущий crc=$FILE_CRC) FAIL"
 exit 2
 fi
 done
done








#######################################################################################
## Отмонтирование USB-носителя с репозиторием
#######################################################################################
if [ $isusb -eq 0 ]; then
 echo "Отмонтирование USB-носителя"
 umount /tmp/repo
 rm /tmp/repo
fi






#######################################################################################
## Аварийное завершение, если нет слоя с ядром
#######################################################################################
if [ $KERNEL_FILE == "NULL" ]; then
 echo "ОШИБКА! В конфигурации не найден слой с ядром! Конфигурация не принята!"
 exit 3
fi





#######################################################################################
## Обработка слоя с ядром
#######################################################################################
echo
echo "Обработка слоя с ядром"
mkdir -p /tmp/kernel
mount $LAYER_ROOT/host/$KERNEL_PATH/$KERNEL_FILE /tmp/kernel
FILE_VERSION="$( ls /tmp/kernel/lib/modules/ )"
FILE_KERNEL="$( ls /tmp/kernel/lib/kernel/ | grep vmlinuz )"
FILE_CRC="$( $LAYER_ROOT/bin/crc32 /tmp/kernel/lib/kernel/$FILE_KERNEL )"

CURRENT_KERNEL="$( uname -r )"

NEED_GRUB_UPDATE=1
if [ -f /boot/$FILE_KERNEL ]; then
    echo "- Ядро $FILE_KERNEL существует на целевой машине!"
    BOOT_CRC="$( $LAYER_ROOT/bin/crc32 /boot/$FILE_KERNEL )"
    if [ $FILE_CRC != $BOOT_CRC ]; then
        echo "ОШИБКА! Ядро $FILE_KERNEL существует на целевой машине и CRC файлов разный! Конфигурация не принята!"
        umount /tmp/kernel
        rm -r /tmp/kernel
        exit 4
    fi
    echo "- CRC файлов одинаковый!"

    if [ $FILE_VERSION == $CURRENT_KERNEL ]; then
        echo "- Распространяемое ядро используется в данный момент на целевой машине по умолчанию! Изменение grub.cfg не требуется."
        NEED_GRUB_UPDATE=0
    fi
else
    echo "- Ядра с таким именем нет на целевой машине."
    cp /tmp/kernel/lib/kernel/$FILE_KERNEL /boot/$FILE_KERNEL
    echo "- Ядро скопировано."
    mkdir -p /lib/modules/$FILE_VERSION
    mount --bind /tmp/kernel/lib/modules/$FILE_VERSION /lib/modules/$FILE_VERSION
    mount --bind /tmp/kernel/lib/firmware /lib/firmware
    echo "- Модули смонтированы"
    
    touch /etc/initramfs-tools/hooks/prometheus
    
    check update-initramfs -c -k $FILE_VERSION
    echo "- Создан initrd для ядра"
    umount /lib/modules/$FILE_VERSION
    umount /lib/firmware
    #rm -Rf /lib/modules/$FILE_VERSION
    echo "- Модули отмонтированы"
fi

umount /tmp/kernel
rm -r /tmp/kernel
echo "- Слой отмонтирован"


KERNEL_NOW_FILE="NULL"
KERNEL_NOW_PATH="NULL"

GRUB_ADDON=""
NEED_PATCH=$( cat /proc/cmdline | grep "test_suspend=mem" | wc -l )

[[ "${NEED_PATCH}" == "1" ]] && GRUB_ADDON="test_suspend=mem"

#UUID="$( cat /proc/cmdline | awk -F " " {'print $2'} | awk -F "=" {'print $3'} )"
UUID=$( blkid | grep root | awk -F " " {'print $3'} | awk -F '"' {'print $2'} )
GRUB_CFG=/boot/grub/grub.cfg

##### Вставка от 27.01.2025 - обработка текущих записей меню загрузки
##    Запоминаем    текущую    конфигурацию    

# Инициализация массива для хранения блоков menuentry
menu_entries=()
# Пока берем только строку названия текущей роли 
MENUENTRY_OLD=""

# Флаг для отслеживания начала и конца блока menuentry
in_menuentry=false
current_entry=""

# Чтение файла построчно
while IFS= read -r line; do
    # Проверка на начало блока menuentry
    if [[ $line == menuentry* ]]; then
        # Если это "Безопасный режим", прекращаем сбор
        if [[ $line == *"Безопасный режим"* ]]; then
            break
        fi
###    		
		MENUENTRY_OLD=$line
		echo "Запомнили прежнюю конфигурацию: - "$MENUENTRY_OLD
		break
###		
        in_menuentry=true
        current_entry="$line"$'\n'
    elif [[ $in_menuentry == true ]]; then
#       if [[ $line == *ATLANT_LAYER_CONFIG=* ]]; then
#			line=$(echo "$line" | sed 's/config\.now/config1\.old/')		
#		fi
		current_entry+="$line"$'\n'
        # Проверка на конец блока menuentry
        if [[ $line == "}" ]]; then
            in_menuentry=false
            menu_entries+=("$current_entry")
            current_entry=""
        fi
    fi
done < "$GRUB_CFG"

##### - Конец вытавки 27.01.2025 ------------

#    Обновление    файла    GRUB_CFG
cat /boot/grub/grub.head >${GRUB_CFG}
echo "- Запись новой конфигурации "$FILE_VERSION

touch /Layer/config.new
DTIME=$( stat -c "%y" /Layer/config.new | head -c 16 )
rm /Layer/config.new

echo "menuentry \"$NAME ($DTIME)\" {" >>$GRUB_CFG
echo -e "\tlinux /$FILE_KERNEL root=UUID=$UUID quiet splash loglevel=0 apparmor=0 ATLANT_MODE=layer ATLANT_LAYER_CONFIG=config.now ${GRUB_ADDON}" >>$GRUB_CFG
echo -e "\tinitrd /initrd.img-$FILE_VERSION" >>$GRUB_CFG
echo "}" >>$GRUB_CFG

echo "" >>$GRUB_CFG

[[ "${NEED_PATCH}" == "1" ]] && sed -i 's/linux \/vmlinuz/linux \/vmlinuz test_suspend=mem/g' "${GRUB_CFG}"

#######################################################################################
## Поиск текущей конфигурации
#######################################################################################
KERNEL_NOW_FILE="NULL"
if [ -e /Layer/config.now ]; then
 mv /Layer/config.now /Layer/config.old
 DTIME=$( stat -c "%y" /Layer/config.old | head -c 16 )
 NOW_LIST="$( cat $LAYER_ROOT/config.old )"
 for row in $(echo "${NOW_LIST}" | $LAYER_ROOT/bin/jq -r '.[] | @base64'); do
	_jq() {
	 echo ${row} | base64 --decode | $LAYER_ROOT/bin/jq -r ${1}
	}
	LAYER_PATH=$(_jq '.path')
	LAYER_FILENAME=$(_jq '.filename')
	LAYER_CRC=$(_jq '.crc')
	LAYER_ISKERNEL=$(_jq '.is_kernel')

	[ "$LAYER_ISKERNEL" == "null" ] && LAYER_ISKERNEL=0

	if [ $LAYER_ISKERNEL -eq 1 ]; then
 	 KERNEL_NOW_FILE=$LAYER_FILENAME
 	 KERNEL_NOW_PATH=$LAYER_PATH
	fi
 done
 
 if [ "$KERNEL_NOW_FILE" != "NULL" ] ; then
     mkdir -p /tmp/kernel
     mount $LAYER_ROOT/host/$KERNEL_NOW_PATH/$KERNEL_NOW_FILE /tmp/kernel
     KERNEL_NOW="$( ls /tmp/kernel/lib/modules/ )"
    
    
     INITRD_NOW="initrd.img-"$KERNEL_NOW
     umount /tmp/kernel
     rm -R /tmp/kernel
     
## Определение количества элементов для вывода пунктов меню

#	num_entries=${#menu_entries[@]}
#	num_to_output=$((num_entries - 2))

## Запись в выходной файл
#	{
#	    for ((i=0; i<num_to_output; i++)); do
## Предварительно необходимо скорректировать название файлов Config_[i].old
#	        echo "${menu_entries[i]}"
#	    done
#   } >> "$GRUB_CFG"
#    echo "Блоки menuentry дополнены."
##   
################### Блок для сдвига резервных конфигураций
 
## Путь к файлам
#config_dir="/Layer"
#current_file="$config_dir/config.now"
#old_file="$config_dir/config.old"

# Функция для сдвига резервных копий
#shift_backups() {
#    for ((i=BACKUP_LEVEL-2; i>=1; i--)); do
#        if [[ -f "$config_dir/config($i).old" ]]; then
#            mv "$config_dir/config($i).old" "$config_dir/config($((i+1))).old"
#        fi
#    done
#    if [[ -f "$old_file" ]]; then
#        mv "$old_file" "$config_dir/config(1).old"
#    fi
#}

## Проверка существования файла config.now
#if [[ -f "$current_file" ]]; then
#    # Сдвиг резервных копий
#    shift_backups

#    # Перенос данных из config.now в config.old
#    cp "$current_file" "$old_file"
#    echo "Данные из $current_file перенесены в $old_file."
#else
#    echo "Файл $current_file не найден."
#fi
################################	

########### Запись прошлых конфигураций
 
     echo "- Запись прошлой конфигурации "$FILE_VERSION
     echo $MENUENTRY_OLD >>$GRUB_CFG
     echo -e "\tlinux /vmlinuz-$KERNEL_NOW root=UUID=$UUID quiet splash loglevel=0 apparmor=0 ATLANT_MODE=layer ATLANT_LAYER_CONFIG=config.old" >>$GRUB_CFG
     echo -e "\tinitrd /$INITRD_NOW" >>$GRUB_CFG
     echo "}" >>$GRUB_CFG
	 
##############################	 
 fi

 
 if [ "$KERNEL_NOW_FILE" = "NULL" ] ; then
     echo "!!! В прошлой конфигурации нет слоя с ядром! "$FILE_VERSION
 fi
 
 
fi

#######################################################################################
## ПОИСК WINDOWS
#######################################################################################
#. "/usr/share/grub/grub-mkconfig_lib"
echo "- Поиск windows на компьютере"
OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`"
if [ ! -z "${OSPROBED}" ] ; then
 for OS in ${OSPROBED} ; do
 WINDEVICE="`echo ${OS} | cut -d ':' -f 1`"
 WINLONGNAME="`echo ${OS} | cut -d ':' -f 2 | tr '^' ' '`"
 WINLABEL="`echo ${OS} | cut -d ':' -f 3 | tr '^' ' '`"
 WINBOOT="`echo ${OS} | cut -d ':' -f 4`"
 WINUUID=$( lsblk -no UUID $WINDEVICE )
 
 case ${WINBOOT} in
 chain)
 case ${WINLONGNAME} in
 Windows*)
 echo "- обнаружена ОС ${WINLONGNAME}"
 echo -e "\n\nmenuentry \"${WINLONGNAME}\" {" >>/boot/grub/grub.cfg
 echo -e "\tinsmod part_msdos" >>/boot/grub/grub.cfg
 echo -e "\tinsmod ntfs" >>/boot/grub/grub.cfg
 echo -e "\tsearch --no-floppy --fs-uuid --set=root ${WINUUID} " >>/boot/grub/grub.cfg
 echo -e "\tdrivemap -s (hd0) \${root}" >>/boot/grub/grub.cfg
 echo -e "\tchainloader +1" >>/boot/grub/grub.cfg
 echo -e "}\n\n" >>/boot/grub/grub.cfg
 
 esac
 ;;
 efi)
 #found_other_os=1
 #EFIPATH=${DEVICE#*@}
 #DEVICE=${DEVICE%@*}
 echo "- обнаружена ОС ${WINLONGNAME}"
 #echo -e "\n\nmenuentry 'Windows' {" >>/boot/grub/grub.cfg
 #prepare_grub_to_access_device ${DEVICE} | sed -e "s/^/\t/" >>/boot/grub/grub.cfg
 #echo -e "\tchainloader ${EFIPATH}" >>/boot/grub/grub.cfg
 #echo -e "}\n\n" >>/boot/grub/grub.cfg
 ;;
 esac
 done
fi

echo $LIST >$LAYER_ROOT/config.now

sed "s/\$UUID/$UUID/g" /boot/grub/grub.footer >>/boot/grub/grub.cfg




#######################################################################################
## Записываем данные пользователей и групп
#######################################################################################
echo Записываем данные пользователей и групп
IDX=1
IFS=':'
read -ra ADDR <<< "$USERS"
for i in "${ADDR[@]}"; do
 FILEWRITE="/dev/null"
 case "$IDX" in
 "1" )
 FILEWRITE="/etc/passwd"
 ;;
 "2" )
 FILEWRITE="/etc/shadow"
 ;;
 "3" )
 FILEWRITE="/etc/group"
 ;;
 "4" )
 FILEWRITE="/etc/gshadow"
 ;;
 esac

 echo "$i" | base64 --decode | gunzip -cf >$FILEWRITE 2>/dev/null

 ((IDX++))
done


#######################################################################################
## Чистка RW от исполняемых файлов
#######################################################################################
rm -Rf /Layer/rw/lib 2>/dev/null
rm -Rf /Layer/rw/bin 2>/dev/null
rm -Rf /Layer/rw/sbin 2>/dev/null

exit 0

