#!/usr/bin/env bash
#
# Author: Dmitry Razumov <asmeron@ublinux.com>
# Copyright (c) 2021-2025 UBLinux <support@ublinux.com>
#
# Initial script for Linux UBLinux
# This script are launching before starting init from initrd script
# Current dir allways must be set to root (/)
# All system path must be relative, except initrd dirs

ENABLED=yes
[[ ${ENABLED} == "yes" ]] || exit 0
DEBUGMODE=no

PATH=.:/:/usr/bin:/usr/local/bin:/usr/local/sbin

[[ -d /usr/lib/ublinux ]] && { ROOTFS= ; CMD_CHROOT= ; } || { [[ -d /sysroot ]] && ROOTFS="/sysroot" || ROOTFS="."; CMD_CHROOT="chroot ${ROOTFS}"; }
SOURCE=${ROOTFS}/usr/lib/ublinux/functions; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null || exit 0
SOURCE=${ROOTFS}/usr/lib/ublinux/default; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null || exit 0
debug_mode "$0" "$@"

SYSCONF="${ROOTFS}${SYSCONF}"
SOURCE=${SYSCONF}/config; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null
SOURCE=${SYSCONF}/security; [ -f ${SOURCE} ] && . ${SOURCE} 2>/dev/null

# Extended pattern matching: https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html#Pattern-Matching
shopt -s extglob

declare -A POLKIT

exec_01_polkit_ubinstall(){
    [[ $(cmdline_value ub.sgnfiles) =~ .*"-iso.sgn" ]] && POLKIT[com.ublinux.ubinstall-gtk.]=yes
}

## Управление разрешениями действий polkit, можно разрешать для группы пользователей
## POLKIT[<id_object>]=<result>[:<user_1>,<user_n>,<@group_1>,<@group_n>:<active>:<local>]"
##   Посмотреть все доступные объекты polkit: pkaction | grep udisks
##   <id_object>        # Имя схемы, применимо использование не полного имени до .(точки)
##     org.freedesktop.udisks2.                            # Mounting a filesystems all subgroup | Монтировать файловую систему все подгруппы
##     org.freedesktop.udisks2.filesystem-mount            # Mounting a filesystems | Монтировать файловую систему
##     org.freedesktop.udisks2.filesystem-mount-system     # Mount a filesystem on a system device | Монтировать файловую систему на системном устройстве
##     org.freedesktop.udisks2.filesystem-mount-other-seat # Mount a device attached to another seat | Монтировать файловую систему с устройства, подключенного в другое место
##     org.freedesktop.udisks2.filesystem-unmount-others   # Unmount a device mounted by another user | Демонтировать устройство, смонтированное другим пользователем
##     org.freedesktop.udisks2.eject-media-other-seat      # Разрешение на извлечение лотка оптического привода пользователям, подключенным не к основному рабочему месту
##     org.freedesktop.udisks2.power-off-drive-other-seat  # Разрешение на извлечение usb-диска пользователям, подключенным не к основному рабочему месту
##     org.freedesktop.machine1.host-login
##     org.freedesktop.DisplayManager.AccountsService.ModifyAny
##     org.freedesktop.login1.suspend
##     org.freedesktop.login1.suspend-multiple-sessions
##     org.freedesktop.login1.suspend-ignore-inhibit
##     org.freedesktop.login1.hibernate
##     org.freedesktop.login1.hibernate-multiple-sessions
##     org.freedesktop.login1.hibernate-ignore-inhibit
##     org.freedesktop.login1.reboot
##     org.freedesktop.login1.reboot-multiple-sessions
##     org.freedesktop.login1.reboot-ignore-inhibit
##     org.freedesktop.login1.set-reboot-parameter
##     org.freedesktop.login1.set-reboot-to-boot-loader-entry
##     org.freedesktop.login1.set-reboot-to-boot-loader-menu
##     org.freedesktop.login1.set-reboot-to-firmware-setup
##     org.freedesktop.login1.manage
##     org.freedesktop.login1.lock-sessions
##     org.freedesktop.login1.chvt
##     org.freedesktop.upower.hibernate
##     org.freedesktop.upower.suspend
##     org.xfce.power.xfce4-pm-helper
##     org.xfce.session.xfsm-shutdown-helper
##     org.manjaro.pamac.                                # GUI Pamac install package | ГУЙ pamac установка и обновление пакетов
##     org.opensuse.cupspkhelper.mechanism.all-edit      # Printer settings | Настройки принтера
##     org.freedesktop.NetworkManager.                   # NetworkManager settings | Настройки NetworkManager
##     org.freedesktop.NetworkManager.settings.modify.system    # Разрешение на создание и модификацию системных сетевых соединений
##   <result>           # Действие на правило
##     yes              # Предоставить разрешения
##     no               # Заблокировать разрешения
##     auth_self        # Пользователь должен ввести свой пароль для аутентификации
##     auth_self_keep   # Пользователь должен ввести свой пароль для аутентификации, авторизация сохраняется на несколько минут
##     auth_admin       # Пользователь должен ввести пароль администратора при каждом запросе
##     auth_admin_keep  # Пользователь должен ввести пароль администратора, авторизация сохраняется на несколько минут
##     null             # Пробовать следующую пользовательскую функцию
##     log              # Сделать запись сообщения о событии в системном журнале
##                      # Сервис polkit перезапускается без параметра --no-debug
##                      # В параметре action передается объект с информацией о совершенном процессе и связанные с этим действием параметры
##                      # В параметре subject передается объект с информацией о пользователе, запустившем процесс. Этот объект имеет следующие атрибуты:
##                      #   id — идентификатор процесса;
##                      #   user — имя пользователя;
##                      #   groups — список групп, в которые входит пользователь;
##                      #   seat — местонахождение субъекта (пустое значение, если местонахождение не локальное);
##                      #   session — сессия субъекта;
##                      #   local — true, только если местонахождение имеет локальный характер;
##                      #   active — true, только если сеанс активен.
##    <user>            # Имена пользователей перечисленные через запятую
##    <@group>          # Имена групп перечисленные через запятую, перед именем группы ставится знак @
##    <active>          # Применять правило только для активных сейнсов
##    <local>           # Применять правило только если местонахождение локальное
##
## POLKIT[org.freedesktop.udisks2.]=yes:@storage
## POLKIT[org.manjaro.pamac.]=yes:@wheel,user-1
## POLKIT[com.ublinux.ubl-settings-datetime.run]=yes:@users:true:true
## POLKIT[org.manjaro.pamac.]=log
## POLKIT[com.ublinux.ubl-settings-kernel.run]=yes,log:@whell,user-1
## POLKIT[org.debian.pcsc-lite.access_pcsc]="yes:@users"
## POLKIT[org.debian.pcsc-lite.access_card]="yes:@users"

exec_02_polkit(){
## Настрока polkit правил
    SYSTEMD_POLKIT_CONF_FILE="${ROOTFS}"/etc/systemd/system/polkit.service.d/ubconfig-59-polkit.conf
    rm -f ${SYSTEMD_POLKIT_CONF_FILE}
    rm -f "${ROOTFS}"/etc/polkit-1/rules.d/ublinux-*
    rm -f "${ROOTFS}"/etc/polkit-1/rules.d/100-ubconfig-*
    local SYSTEMD_POLKIT_CONF_MAKE=
    if [[ -n ${POLKIT[@]} ]]; then
	for RULES in "${!POLKIT[@]}"; do
	    local IN_RULES_RESULT= IN_RULES_USERGROUP= IN_RULES_ACTIVE= IN_RULES_LOCAL=
	    local OUT_RULES_IF= OUT_RULES_LOG= OUT_RULES_RESULT=
	    local ALLOW_RULES_RESULT=
	    OUT_RULES_FILE="${ROOTFS}/etc/polkit-1/rules.d/100-ubconfig-$(sed -E -e 's/([^\.]*)\.([^\.]*)\.([^\.]+)\..*/\1-\2-\3/' -e 's/\./-/g' <<< ${RULES}).rules"
	    : ${OUT_RULES_FILE:+${OUT_RULES_FILE//-./.}}
	    IFS=: read -r IN_RULES_RESULT IN_RULES_USERGROUP IN_RULES_ACTIVE IN_RULES_LOCAL <<< "${POLKIT[${RULES}]}"
	    # Проверка на верность заданных значений
	    [[ -n ${IN_RULES_RESULT} ]] && while IFS= read -ru3 SELECT_RULES_RESULT; do
		[[ ${SELECT_RULES_RESULT,,} == @(yes|no|auth_self|auth_self_keep|auth_admin|auth_admin_keep|null|log) ]] && ALLOW_RULES_RESULT=yes
	    done 3<<< ${IN_RULES_RESULT//@(,|;)/$'\n'}
	    # Если нет разрешённого RULES_RESULT, то следующий параметр
	    [[ -n ${ALLOW_RULES_RESULT} ]] || continue
	    [[ -n ${IN_RULES_ACTIVE} && ${IN_RULES_ACTIVE,,} != @(yes|true|enable) ]] && continue
	    [[ -n ${IN_RULES_LOCAL} && ${IN_RULES_LOCAL,,} != @(yes|true|enable) ]] && continue
	    #
	    [[ ${IN_RULES_ACTIVE,,} == @(yes|true|enable) ]] && OUT_RULES_IF+=$'\n'$'\t'"&& subject.active == true"
	    [[ ${IN_RULES_LOCAL,,} == @(yes|true|enable) ]] && OUT_RULES_IF+=$'\n'$'\t'"&& subject.local == true"
	    [[ -n ${IN_RULES_USERGROUP} ]] && while IFS= read -ru3 SELECT_USERGROUP; do
		if [[ ${SELECT_USERGROUP:0:1} == "@" ]]; then
		    OUT_RULES_IF+=$'\n'$'\t'"&& subject.isInGroup(\"${SELECT_USERGROUP:1}\")"
		else
		    OUT_RULES_IF+=$'\n'$'\t'"&& subject.user == \"${SELECT_USERGROUP}\""
		fi
	    done 3<<< ${IN_RULES_USERGROUP//@(,|;)/$'\n'}
	    [[ -n ${IN_RULES_RESULT} ]] && while IFS= read -ru3 SELECT_RULES_RESULT; do
		[[ ${SELECT_RULES_RESULT,,} == "log" ]] && OUT_RULES_LOG+=$'\n'$'\t''polkit.log("action=" + action);'$'\n'$'\t''polkit.log("subject=" + subject);' && SYSTEMD_POLKIT_CONF_MAKE=yes
		[[ ${SELECT_RULES_RESULT,,} == @(yes|no|auth_self|auth_self_keep|auth_admin|auth_admin_keep|null) ]] && OUT_RULES_RESULT+=$'\n'$'\t'"return polkit.Result.${SELECT_RULES_RESULT^^};"
	    done 3<<< ${IN_RULES_RESULT//@(,|;)/$'\n'}
	    cat >> ${OUT_RULES_FILE} <<EOF
polkit.addRule(function(action, subject) {
    if (action.id.indexOf("${RULES}") == 0${OUT_RULES_IF}
	)
    {	${OUT_RULES_LOG}${OUT_RULES_RESULT}
    }
});
EOF
	done
	ISSYSTEMD=$(readlink -fq ${ROOTFS}/usr/bin/init | grep "lib/systemd/systemd$")
	if [[ -n ${ISSYSTEMD} && -n ${SYSTEMD_POLKIT_CONF_MAKE} ]]; then
	    install -Dm644 /dev/stdin ${SYSTEMD_POLKIT_CONF_FILE} <<-EOF
[Service]
ExecStart=
ExecStart=-/usr/lib/polkit-1/polkitd
EOF
	    # Перезапуск сервиса polkit.service
	    # Если выполнение в initrd, то не выполнять
	    [[ -z ${ROOTFS} ]] && systemctl daemon-reload && systemctl restart polkit.service
	fi
    fi
}


################
##### MAIN #####
################

    # Если файл подключен как ресурс с функциями, то выйти
    return 0 2>/dev/null && return 0
    if [[ -z $@ ]]; then
        while read -r FUNCTION; do
            $"${FUNCTION##* }"
        done < <(declare -F | grep "declare -f exec_")
    else
        FUNCTION=
        while [[ $# -gt 0 ]]; do
            #[[ -z ${1} ]] || { declare -f "${1}" &>/dev/null && FUNCTION+="; ${1}" || FUNCTION+=" '${1//\'/}'"; }
            # Что-бы передавать пустые параметры как аргументы, нужно для соблюдения очередности и кол-ва, отключил [[ -z ${1} ]] ||
            declare -f "${1}" &>/dev/null && FUNCTION+="; ${1}" || FUNCTION+=" '${1//\'/}'"
            shift
        done
        eval ${FUNCTION#*; }
    fi
