#!/usr/bin/env bash
#
# Initial script for 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

unset ROOTFS CMD_CHROOT; [[ -d /usr/lib/ublinux ]] || { [[ -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}/users; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null
SOURCE=${SYSCONF}/system; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null

FILE_ROOT_USERS="${SYSCONF}/.users_credential"
SOURCE=${FILE_ROOT_USERS}; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null

    FILE_PASSWD="${ROOTFS}/etc/passwd"
    FILE_SHADOW="${ROOTFS}/etc/shadow"
    FILE_GROUP="${ROOTFS}/etc/group"
    FILE_GSHADOW="${ROOTFS}/etc/gshadow"
    PATH_HOME="/home"

# Задать пароль пользователю
#  $1	# Имя пользователя
#  $2	# Зашифрованный пароля, если не указан, то пароль '!*' запрет авторизации
set_passwd(){
    USER_NAME="${1}"
    USER_PASS="${2}"
    [[ ${USER_NAME} != "" ]] || return 1
    [[ ${USER_PASS} != "" ]] || USER_PASS='!*'
    if [[ -x ${ROOTFS}/usr/bin/chpasswd ]]; then
	echo "${USER_NAME}:${USER_PASS}" | ${CMD_CHROOT} /usr/bin/chpasswd --encrypted
    else
	ESC_USER_PASS=$(sed 's/[^a-zA-Z0-9,_@%-]/\\&/g' <<< "${USER_PASS}")
	EPOCH_DAY=$(( $(date +%s)/(60*60*24) )) # (60*60*24)=18400 second on day
	USER_FROM_SHADOW=$(grep "^${USER_NAME}:" "${FILE_SHADOW}")
	if [[ -z ${USER_FROM_SHADOW} ]]; then
    	    echo "${USER_NAME}:${USER_PASS}:${EPOCH_DAY}:0:99999:7:::" >> "${FILE_SHADOW}"
	elif [[ ! ${USER_FROM_SHADOW} =~ ^"${USER_NAME}:${USER_PASS}:" ]]; then
    	    sed -E "s/^${USER_NAME}:[^:]+:[0-9]+:/${USER_NAME}:${ESC_USER_PASS}:${EPOCH_DAY}:/" -i "${FILE_SHADOW}"
    	    sed -E "s/${USER_NAME}:[!]*:/${USER_NAME}:\!\*:/" -i "${FILE_SHADOW}"
    	    #sed /^${USER_NAME}:/d -i "${FILE_SHADOW}"
    	    #echo "${USER_NAME}:${USER_PASS}:${EPOCH_DAY}:0:99999:7:::" >> "${FILE_SHADOW}"
	fi
    fi
}

# Задать пароль группе
#  $1	# Имя группы
#  $2	# Зашифрованный пароля, если не указан, то пароль '!*' запрет авторизации
#set_gpasswd(){
#    GROUP_NAME="${1}"
#    GROUP_PASS="${2}"
#    [[ ${GROUP_NAME} != "" ]] || return 1
#    [[ ${GROUP_PASS} != "" ]] || GROUP_PASS="!*"
#    if [[ -x ${ROOTFS}/usr/bin/chgpasswd ]]; then
#	echo "${GROUP_NAME}:${GROUP_PASS}" | ${CMD_CHROOT} /usr/bin/chgpasswd --encrypted
#    else
#	ESC_GROUP_PASS=$(sed 's/[^a-zA-Z0-9,_@%-]/\\&/g' <<< "${GROUP_PASS}")
#	GROUP_FROM_SHADOW=$(grep "^${GROUP_NAME}:" "${FILE_GSHADOW}")
#	if [[ -z ${GROUP_FROM_SHADOW} ]]; then
#    	    echo "${GROUP_NAME}:${GROUP_PASS}::" >> "${FILE_GSHADOW}"
#	elif [[ ! ${GROUP_FROM_SHADOW} =~ ^"${GROUP_NAME}:${GROUP_PASS}:" ]]; then
#    	    sed -E "s/^${GROUP_NAME}:[^:]*:/${GROUP_NAME}:${ESC_GROUP_PASS}:/" -i "${FILE_GSHADOW}"
#	fi
#    fi
#}

# Создать домашний каталог и копировать /etc/skel в домашний каталог пользователя
#  $1	# Имя пользователя
#  $2	# Основная группа пользователя, если не указана, то соответствует имени пользователя
#  $3	# Если указано, то принудительно копировать /etc/skel в домашний каталог пользователя
create_home(){
    local SELECT_USERNAME="${1}"
    local SELECT_GROUP="${2}"
    local SELECT_FORCESKEL="${3}"
    local ARG_RECURSIVE=
    [[ ${SELECT_GROUP} == "" || ${SELECT_GROUP} == "-" ]] && SELECT_GROUP=${SELECT_USERNAME}
    [[ ${SELECT_USERNAME} != "" ]] || return 1
    if [[ -d "${ROOTFS}${PATH_HOME}/${SELECT_USERNAME}" ]]; then
	${CMD_CHROOT} /usr/bin/chmod -f u+rw,g-rwx,o-rwx "${PATH_HOME}/${SELECT_USERNAME}"
	[[ -z ${SELECT_FORCESKEL} ]] || cp -Taf ${ROOTFS}/etc/skel "${ROOTFS}${PATH_HOME}/${SELECT_USERNAME}"
    else 
	install -dm700 "${ROOTFS}${PATH_HOME}/${SELECT_USERNAME}"
	cp -Taf ${ROOTFS}/etc/skel "${ROOTFS}${PATH_HOME}/${SELECT_USERNAME}"
    fi
    #rsync  -rlpt --ignore-existing etc/skel/ "${ROOTFS}${PATH_HOME}/${SELECT_USERNAME}"
    [[ -n ${ROOTFS} ]] && ARG_RECURSIVE="-R" || unset ARG_RECURSIVE
    ${CMD_CHROOT} /usr/bin/chown -f ${ARG_RECURSIVE} "${SELECT_USERNAME}" ${PATH_HOME}/"${SELECT_USERNAME}"
    ${CMD_CHROOT} /usr/bin/chown -f ${ARG_RECURSIVE} :"${SELECT_GROUP}" "${PATH_HOME}/${SELECT_USERNAME}"
}

# Задаём пароль root пользователю
exec_00_defaultrootpasswd(){
#echo "exec_02_defaultrootpasswd"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local PARAM="$@"
    if [[ -n ${PARAM} ]]; then
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ -n ${DEFAULTROOTPASSWD} && ! ${DEFAULTROOTPASSWD,,} == @(no|none|disable) ]]; then
        # Добавить параметр в ${FILE_ROOT_USERS}=.users_credential и удалить параметр DEFAULTROOTPASSWD из '/etc/ublinux/users
	if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
	    if [[ -f ${FILE_ROOT_USERS} ]]; then
	        sed "/DEFAULTROOTPASSWD=/d" -i "${FILE_ROOT_USERS}"
	        echo "DEFAULTROOTPASSWD='${DEFAULTROOTPASSWD}'" >> ${FILE_ROOT_USERS}
	    fi
	    [[ -f "${SYSCONF}/users" ]] && sed "/DEFAULTROOTPASSWD=/d" -i "${SYSCONF}/users"
	fi
	[[ -n ${DEFAULTROOTPASSWD} ]] && DEFAULTROOTPASSWD=$(return_hash_password hash ${HASHPASSWD} ${DEFAULTROOTPASSWD})
	set_passwd root "${DEFAULTROOTPASSWD}"
    elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then
	if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
	    [[ -f ${FILE_ROOT_USERS} ]] && sed "/DEFAULTROOTPASSWD=/d" -i "${FILE_ROOT_USERS}"
	    [[ -f "${SYSCONF}/users" ]] && sed "/DEFAULTROOTPASSWD=/d" -i "${SYSCONF}/users"
	fi
	
    fi
}

# Задаём пароль по умолчанию пользователю
exec_00_defaultpasswd(){
#echo "exec_02_defaultrootpasswd"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local PARAM="$@"
    if [[ -n ${PARAM} ]]; then
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ -n ${DEFAULTPASSWD} ]]; then
        # Добавить параметр в ${FILE_ROOT_USERS}=.users_credential и удалить параметр DEFAULTROOTPASSWD из '/etc/ublinux/users
	if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
	    if [[ -f ${FILE_ROOT_USERS} ]]; then
	        sed "/DEFAULTPASSWD=/d" -i "${FILE_ROOT_USERS}"
	        echo "DEFAULTPASSWD='${DEFAULTPASSWD}'" >> ${FILE_ROOT_USERS}
	    fi
	    [[ -f "${SYSCONF}/users" ]] && sed "/DEFAULTPASSWD=/d" -i "${SYSCONF}/users"
	fi
    elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then
	if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
	    [[ -f ${FILE_ROOT_USERS} ]] && sed "/DEFAULTPASSWD=/d" -i "${FILE_ROOT_USERS}"
	    [[ -f "${SYSCONF}/users" ]] && sed "/DEFAULTPASSWD=/d" -i "${SYSCONF}/users"
	fi
	
    fi
}

# Создаём группы из ${DEFAULTGROUP},${ADMGROUPS},${USERGROUPS} c ID из /usr/share/ublinux-sysusers/*.sysusers
exec_01_add_groups(){
#echo "exec_01_add_groups"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local PARAM="$@"
    local GROUPADD_GROUPS SELECT_GROUP ARG_FINDGROUP_ID FINDGROUP_ID
    if [[ -n ${PARAM} ]]; then
	GROUPADD_GROUPS=${PARAM}
    else
	GROUPADD_GROUPS="${DEFAULTGROUP},${ADMGROUPS},${USERGROUPS}"
    fi
    if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ -n ${GROUPADD_GROUPS} ]]; then
	GROUPADD_GROUPS=${GROUPADD_GROUPS//;/,}; GROUPADD_GROUPS="${GROUPADD_GROUPS//,,/,}"
	[[ ${GROUPADD_GROUPS:0:1} == ',' ]] && GROUPADD_GROUPS=${GROUPADD_GROUPS:1}
	[[ ${GROUPADD_GROUPS} =~ ','$ ]] && GROUPADD_GROUPS=${GROUPADD_GROUPS%*,}
	[[ -n ${GROUPADD_GROUPS} ]] && while IFS= read -u3 SELECT_GROUP; do
	    unset ARG_FINDGROUP_ID
	    # Найти группу по имени
	    [[ $(cat ${ROOTFS}/usr/share/ublinux-sysusers/*.sysusers) =~ ($'\n'|^)+'g'[[:blank:]]+"${SELECT_GROUP}"[[:blank:]]+([[:digit:]]+)[^$'\n']*($'\n'|$)+ ]] && FINDGROUP_ID=${BASH_REMATCH[2]} || FINDGROUP_ID=
	    # Найти группу по GUID
	    #[[ $(cat ${ROOTFS}/usr/share/ublinux-sysusers/*.sysusers) =~ ($'\n'|^)+'g'[[:blank:]]+([^$'\n']+)[[:blank:]]+"${SELECT_GROUP}"[^$'\n']*($'\n'|$)+ ]] && FINDGROUP_NAME=${BASH_REMATCH[2]}
	    if [[ ${FINDGROUP_ID} != "" && $(cat ${FILE_GROUP} 2>/dev/null) =~ ($'\n'|^)+${SELECT_GROUP}:[^$'\n']*:${FINDGROUP_ID}:[^$'\n']*($'\n'|$)+ ]]; then
	    # Группа найдена, имя и id совпадают, пропускаем добавление
		    continue
	    elif [[ ${FINDGROUP_ID} != "" && $(cat ${FILE_GROUP} 2>/dev/null) =~ ($'\n'|^)+${SELECT_GROUP}:[^$'\n']*($'\n'|$)+ ]]; then
	    # Группа найдена, имя и id не совпадают, удаляем группу
		echo "WARNING: the group '${SELECT_GROUP}' has an id different from the template /usr/share/ublinux-sysusers/*.sysusers and the id will be changed to '${SELECT_GROUP}:${FINDGROUP_ID}'"
		${CMD_CHROOT} /usr/bin/groupdel -f ${SELECT_GROUP}
	    fi
	    [[ ${FINDGROUP_ID} == @(""|"-") ]] || ARG_FINDGROUP_ID="--gid ${FINDGROUP_ID}"
	    ${CMD_CHROOT} /usr/bin/groupadd --force ${ARG_FINDGROUP_ID} ${SELECT_GROUP}
	done 3<<< "${GROUPADD_GROUPS//,/$'\n'}"
    fi
}


# Создаем пользователей из ${NEEDEDUSERS} и добавляем в группы
# $1    Команды set или remove с режимом, варианты: set=|set+=|set++=|set-=|set--=|remove
# $2    Для команды set=|set+=|set++= параметр со значением, пример: 
#       Для команды set-=|set--=|remove параметр только с именем, пример:
# null  Если отсутствует $@, то применяем из системной конфигурации 
exec_02_neededusers(){
#echo "exec_03_neededusers"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local PARAM="$@"
    local SELECT_USERNAME SELECT_UID SELECT_PASSWORD SELECT_GECOS NULL ADDGROUPS
    local ARG_DEFAULTGROUP ARG_SELECT_UID ARG_SELECT_GECOS
    if [[ -n ${PARAM} ]]; then
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    # Если по умолчанию нет ни одного пользователя, то создаём администратора
    #[[ -z ${NEEDEDUSERS} ]] && NEEDEDUSERS="${DEFAULTUSER}:${ADMUID}:${DEFAULTPASSWD}:Administrator"
    [[ -z $(cmdline_value users) ]] || NEEDEDUSERS=$(cmdline_value users)
    [[ ${NOSECUREROOTPASSWD} == ${DEFAULTROOTPASSWD} ]] && ADDADM=yes
    if [[ -n ${NEEDEDUSERS} ]]; then
	while IFS= read -ru3 SELECT_USER; do
	    # Добавить параметр в ${FILE_ROOT_USERS}=.users_credential и удалить параметр NEEDEDUSERS из '/etc/ublinux/users
	    if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
		if [[ -f ${FILE_ROOT_USERS} ]]; then
		    sed "/NEEDEDUSERS=/d" -i "${FILE_ROOT_USERS}"
		    echo "NEEDEDUSERS='${SELECT_USER}'" >> ${FILE_ROOT_USERS}
		fi
		[[ -f "${SYSCONF}/users" ]] && sed "/NEEDEDUSERS=/d" -i "${SYSCONF}/users"
	    fi
	    IFS=: read -r SELECT_USERNAME SELECT_UID SELECT_PASSWORD SELECT_GECOS NULL <<< "${SELECT_USER}"
	    [[ ${SELECT_PASSWORD} == "x" ]] && SELECT_PASSWORD="${DEFAULTPASSWD}"
	    ADDGROUPS="${USERGROUPS}"
	    [[ ${SELECT_UID} == ${ADMUID} && ${ADDADM} == "yes" ]] && ADDGROUPS="${USERGROUPS},${ADMGROUPS}"
	    # Если указана обязательная синхронизация при каждом запуске, то пользователя удалить и создать нового
	    if [[ ${USERADD_SYNC} =~ 'boot' || ${USERADD_SYNC[${SELECT_USERNAME}]} =~ 'boot' ]]; then
		if [[ -x ${ROOTFS}/usr/bin/userdel ]]; then
		    ${CMD_CHROOT} /usr/bin/userdel -f ${SELECT_USERNAME}
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		# busybox deluser
		    ${CMD_CHROOT} /usr/bin/busybox deluser ${SELECT_USERNAME}
		fi
	    fi
	    # Создаём пользователя
	    if ! grep -q ^"${SELECT_USERNAME}": ${FILE_PASSWD} 2>/dev/null; then
		[[ -n ${SELECT_UID} ]] && ARG_SELECT_UID="-u ${SELECT_UID}" || unset ARG_SELECT_UID
		if [[ -x ${ROOTFS}/usr/bin/useradd ]]; then
		    [[ -n ${SELECT_GECOS} ]] && ARG_SELECT_GECOS="-c ${SELECT_GECOS}" || unset ARG_SELECT_GECOS
		    [[ -n ${DEFAULTGROUP} ]] && ARG_DEFAULTGROUP="-G ${DEFAULTGROUP}" || unset ARG_DEFAULTGROUP
		    ${CMD_CHROOT} /usr/bin/useradd -M ${ARG_DEFAULTGROUP} ${ARG_SELECT_UID} ${ARG_SELECT_GECOS} ${SELECT_USERNAME} #>/dev/null 2>&1
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		# busybox adduser
		    [[ -n ${SELECT_GECOS} ]] && ARG_SELECT_GECOS="-g ${SELECT_GECOS}" || unset ARG_SELECT_GECOS
		    [[ -n ${SELECT_GROUP} ]] && ARG_SELECT_GROUP="-G ${SELECT_GROUP}" || ARG_SELECT_GROUP="-G ${SELECT_USERNAME}"
		    ${CMD_CHROOT} /usr/bin/busybox adduser -D -H "${ARG_DEFAULTGROUP}" "${ARG_SELECT_UID}" "${ARG_SELECT_GECOS}" "${SELECT_USERNAME}" #>/dev/null 2>&1
		fi
		# Добавляем пользователя в группу
		USER_GROUPS="${ADDGROUPS//;/,}"
		${CMD_CHROOT} /usr/bin/usermod -a -G ${USER_GROUPS%*,} ${SELECT_USERNAME} #>/dev/null 2>&1
		# Задаём пароль пользователю
		#[[ -n ${SELECT_PASSWORD} ]] && return_hash_password "${SELECT_PASSWORD}" && [[ -n ${HASH_PASSWORD_NEW} ]] && SELECT_PASSWORD="${HASH_PASSWORD_NEW}"
		[[ -n ${SELECT_PASSWORD} ]] && SELECT_PASSWORD=$(return_hash_password hash ${HASHPASSWD} ${SELECT_PASSWORD})
		set_passwd "${SELECT_USERNAME}" "${SELECT_PASSWORD}"
	    fi
	    # Создаём домашний каталог принудительно и копируем /etc/skel
	    if [[ ! -d ${ROOTFS}${PATH_HOME}/"${SELECT_USERNAME}" \
	    || ${UPDATEHOME[${SELECT_USERNAME}],,} == @(yes|y|enable) \
	    || ${UPDATEHOME,,} == @(yes|y|enable) ]]; then
	        create_home "${SELECT_USERNAME}" - force
	    fi
	done 3< <(tr ",;" "\n" <<< "${NEEDEDUSERS}")
    fi
}

#declare -A UPDATEHOME
#UPDATEHOME[user-4]=yes
#declare -A USERADD_SYNC
#USERADD_SYNC[user-4]="shutdown,boot"
#USERADD_SYNC="shutdown,boot"
#declare -A USERADD 
#USERADD[superadmin]='Администратор:1000:x:x:x:$6$E7stRhRS8fCKk7UU$Qoqw62AUaUa5uLIc2KC7WV3MUThhrR8kjXtCODmnKCzKe2zHu1/wmsiWBHZEIk/IQnk/aELQYbUK93OUtrwg60'
#USERADD[user-1]='Пользователь-1:x:x:vboxusers,libvirt:-s /usr/bin/bash -o:$6$E7stRhRS8fCKk7UU$Qoqw62AUaUa5uLIc2KC7WV3MUThhrR8kjXtCODmnKCzKe2zHu1/wmsiWBHZEIk/IQnk/aELQYbUK93OUtrwg60'
#USERADD[user-2]='Пользователь-2::users:vboxusers,libvirt:-s /usr/bin/bash -o:$6$E7stRhRS8fCKk7UU$Qoqw62AUaUa5uLIc2KC7WV3MUThhrR8kjXtCODmnKCzKe2zHu1/wmsiWBHZEIk/IQnk/aELQYbUK93OUtrwg60'
#USERADD[user-3]=':::::x'
#USERADD[user-4]='Пользователь-4::users:user-2,user-3:-s /usr/bin/sh:$6$E7stRhRS8fCKk7UU$Qoqw62AUaUa5uLIc2KC7WV3MUThhrR8kjXtCODmnKCzKe2zHu1/wmsiWBHZEIk/IQnk/aELQYbUK93OUtrwg60'
#declare -A USERSHADOW
#USERSHADOW[user-4]=19695:0:99999:7::
#USERSHADOW[user-4]=18000:0:120:7:14:
#USERSHADOW[user-4]="2023-12-31:0:120:7:14:2024-12-31"

# Добавить пользователя системы в /etc/passwd. Если пользователь существует, то без изменений
# $1    Команды set или remove с режимом, варианты: set=|set+=|set++=|set-=|set--=|remove
# $2    Для команды set=|set+=|set++= параметр со значением, пример: USERADD[user-1]='Пользователь-1:x:x:vboxusers,libvirt:-s /usr/bin/bash:%%plain_password'
#       Для команды set-=|set--=|remove параметр только с именем, пример: USERADD[superadmin]
# null  Если отсутствует $@, то применяем из системной конфигурации USERADD[*]
##
## USERADD=no|none|disable      # Отключить управление прользователями конфигурации
## USERADD[user_name]='gecos:uid:user_group:extra_groups:optional:password|x'
##   user_name          # Имя пользователя, обязательное поле
##   gecos              # Поле GECOS, с подробным описанием пользователя, можно локализованное, не обязательное
##   uid                # UID пользователя, если необходимо автоматически рассчитывать, то оставить пустым или 'x'
##   user_group         # Основная группа пользователя, номер или имя, если выбрано пусто или 'x', то 'user_group=user_name'
##   extra_groups       # Дополнительные группы пользователя. Дополнительные к USERGROUPS
##                      # Если группа отсутствует или 'x', то 'extra_groups=users'. Если группа не существует, то будет создана. Перечисление через запятую. 
##   optional           # Дополнительные параметры, например: '--shell /usr/bin/bash --create-home --no-create-home --no-user-group --non-unique'
##     --home-dir <ДОМ_КАТ>      # Домашний каталог новой учётной записи
##     -s, --shell /usr/bin/bash # Регистрационная оболочка новой учётной записи
##     -r, --system              # Создавать системную группу
##     -M, --no-create-home      # Не создавать домашний каталог пользователя
##     -N, --no-user-group       # Не создавать группу с тем же именем что и у пользователя
##     -o, --non-unique          # Разрешить создание пользователей с повторяющимися (не уникальными) UID, использовать только совместно с параметром <uid>
##     --badnames                # Не проверять имя на несоответствие правилам использования символов  
##   password|x         # Хеш пароля пользователя, если пусто или 'x', то 'password=${DEFAULTPASSWD}', 
##                      # Если первые символы (%%), то пароль хранится в нешифрованном виде
##                      # Если первые символы (!*), то  аутентификация запрещена
##                      # Если первый символ (*) или (!), то аутентификация по паролю заблокирована. Но другие методы входа, 
##                      # такие как аутентификация на основе ключей или переключение на пользователя, по-прежнему разрешены
## USERADD[superadmin]='Администратор:1000:x:x:x:$6$E7stRhRS8fCKk7UU$Qoqw62AUaUa5uLIc2KC7WV3MUThhrR8kjXtCODmnKCzKe2zHu1/wmsiWBHZEIk/IQnk/aELQYbUK93OUtrwg60'
## USERADD[user-1]=x
## USERADD[user-1]='Пользователь-1:x:x:vboxusers,libvirt:-s /usr/bin/bash:%%plain_password'
##
## Синхронизация пользователей системы /etc/passwd с глобальной конфигурацией
## USERADD_SYNC[user_name]='boot,shutdown'
##   user_name		# Имя пользователя, необязательное поле. Если не указано, то применяется для всех пользователей
##   boot		# При загрузке системы принудительно применить глобальную конфигурацию на пользователя
##   shutdown		# При завершении работы системы синхронизировать указанных пользователей в системе с глобальной конфигурацией
exec_03_useradd(){
#echo "exec_03_useradd"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="

    [[ ${USERADD} != @(none|no|disable) ]] || return 0
    [[ -n ${USERADD_SYNC} ]] || declare -gA USERADD_SYNC 
    [[ -n ${UPDATEHOME} ]] || declare -gA UPDATEHOME
    [[ -n ${USERADD} ]] || declare -gA USERADD
    declare -gA USERSHADOW
    local SELECT_USERNAME SELECT_GECOS SELECT_UID SELECT_GROUP SELECT_EXTRAGROUP SELECT_OPTIONAL SELECT_PASSWORD NULL
    local ARG_SELECT_UID ARG_SELECT_GROUP ARG_SELECT_GECOS ARG_SELECT_PASSWORD ARG_SELECT_OPTIONAL
    local ARG_GROUPADD_GID ARG_GROUPADD_GROUPNAME
    local PARAM="$@"
    if [[ -n ${PARAM} ]]; then
	local USERADD=
	declare -A USERADD
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    # Если в GRUB указан параметр useradd, то создать пользователя
    while IFS=':' read -u3 SELECT_USERNAME SELECT_UID SELECT_GROUP SELECT_EXTRAGROUP SELECT_PASSWORD NULL; do
	[[ ${SELECT_USERNAME} != "" ]] && USERADD[${SELECT_USERNAME}]=":${SELECT_UID}:${SELECT_GROUP}:${SELECT_EXTRAGROUP}:x:${SELECT_PASSWORD}"
    done 3< <(tr ';' '\n' <<< $(cmdline_value useradd))
    if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]]; then
	[[ ${#USERADD[@]} == 0 ]] && USERADD[${DEFAULTUSER}]="Administrator:${ADMUID}:x:x:x:${DEFAULTPASSWD}"
	exec_05_groupadd
	[[ ${NOSECUREROOTPASSWD} == ${DEFAULTROOTPASSWD} ]] && ADDADM=yes
        while IFS= read -ru3 SELECT_USERNAME; do
	    # Добавить параметр в ${FILE_ROOT_USERS}=.users_credential и удалить параметр USERADD[.*] из '/etc/ublinux/users
	    if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
		if [[ -f ${FILE_ROOT_USERS} ]]; then
		    sed "/USERADD\[${SELECT_USERNAME}\]=/d" -i "${FILE_ROOT_USERS}"
		    echo "USERADD[${SELECT_USERNAME}]='${USERADD[${SELECT_USERNAME}]}'" >> ${FILE_ROOT_USERS}
		fi
		[[ -f "${SYSCONF}/users" ]] && sed "/USERADD\[.*\]=/d" -i "${SYSCONF}/users"
	    fi
	    IFS=: read -r SELECT_GECOS SELECT_UID SELECT_GROUP SELECT_EXTRAGROUPS SELECT_OPTIONAL SELECT_PASSWORD NULL <<< "${USERADD[${SELECT_USERNAME}]}"
	    [[ ${SELECT_GECOS,,} == "x" ]] && unset SELECT_GECOS
	    [[ ${SELECT_UID,,} == "x" || ${SELECT_UID} =~ ^[^0-9]+$ ]] && unset SELECT_UID
	    [[ ${SELECT_GROUP,,} == "x" ]] && unset SELECT_GROUP
	    [[ ${SELECT_EXTRAGROUPS,,} == "x" ]] && unset SELECT_EXTRAGROUPS
	    [[ ${SELECT_OPTIONAL,,} == "x" ]] && unset SELECT_OPTIONAL
	    [[ ${SELECT_PASSWORD} == @(""|"x") ]] && SELECT_PASSWORD="${DEFAULTPASSWD}"
	    [[ ${SELECT_PASSWORD} != @(""|'!*'|'!'|'*') ]] && SELECT_PASSWORD=$(return_hash_password hash ${HASHPASSWD} ${SELECT_PASSWORD})
	    # Если в дополнительных группа присутствует группа по имени пользователя, то удалить её из списка
	    SELECT_EXTRAGROUPS=${SELECT_EXTRAGROUPS//${SELECT_USERNAME}/}
	    # Создадать группы из параметра
	    [[ ${SELECT_EXTRAGROUPS} == "" ]] || exec_01_add_groups "${SELECT_EXTRAGROUPS}"
	    SELECT_EXTRAGROUPS="${SELECT_EXTRAGROUPS},${USERGROUPS}"
	    [[ ${SELECT_UID} == ${ADMUID} && ${ADDADM} == "yes" ]] && SELECT_EXTRAGROUPS="${SELECT_EXTRAGROUPS},${ADMGROUPS}"
	    SELECT_EXTRAGROUPS="${SELECT_EXTRAGROUPS//;/,}"; SELECT_EXTRAGROUPS="${SELECT_EXTRAGROUPS//,,/,}"
	    [[ ${SELECT_EXTRAGROUPS:0:1} == "," ]] && SELECT_EXTRAGROUPS=${SELECT_EXTRAGROUPS:1}
#echo "===> ${SELECT_USERNAME}=${SELECT_GECOS}:${SELECT_UID}:${SELECT_GROUP}:${SELECT_EXTRAGROUPS}:${SELECT_OPTIONAL}:${SELECT_PASSWORD}"
#echo "===> ${SELECT_USERNAME}=${SELECT_GECOS}:${SELECT_UID}:${SELECT_GROUP}:${SELECT_EXTRAGROUPS}:${SELECT_OPTIONAL}"

	    # Если указана обязательная синхронизация при каждом запуске, то пользователя удалить и создать нового
	    if [[ ${USERADD_SYNC} =~ 'boot' || ${USERADD_SYNC[${SELECT_USERNAME}]} =~ 'boot' ]]; then
		if [[ -x ${ROOTFS}/usr/bin/userdel ]]; then
		    ${CMD_CHROOT} /usr/bin/userdel -f "${SELECT_USERNAME}" 2>/dev/null
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		# busybox deluser
		    ${CMD_CHROOT} /usr/bin/busybox deluser ${SELECT_USERNAME} 2>/dev/null
		fi
	    fi
	    # Проверяем наличие пользователя в системе
	    ARG_SELECT_UID=; ARG_SELECT_GROUP=; ARG_SELECT_GECOS=; ARG_SELECT_PASSWORD=; ARG_SELECT_OPTIONAL=;
	    if [[ ! $(cat ${FILE_PASSWD} 2>/dev/null) =~ ($'\n'|^)+"${SELECT_USERNAME}": ]]; then
		[[ -n ${SELECT_UID} ]] && ARG_SELECT_UID="--uid ${SELECT_UID}" || unset ARG_SELECT_UID
		# Если указана основная группа, но она не создана, то создать
		unset ARG_GROUPADD_GID ARG_GROUPADD_GROUPNAME
		# Если группа не найдена
		if [[ -n ${SELECT_GROUP} && ! $(cat ${FILE_GROUP}) =~ ($'\n'|^)+(${SELECT_GROUP}:|[^$'\n']*:${SELECT_GROUP}:) ]]; then
		    # Группа имет цифровой GID и номер GID=UID
		    if [[ ${SELECT_GROUP} =~ ^[[:digit:]]+$ && ${SELECT_GROUP} == ${SELECT_UID} ]]; then
			#ARG_GROUPADD_GID=" --gid ${SELECT_GROUP}"
			ARG_GROUPADD_GID="${SELECT_GROUP}"
			ARG_GROUPADD_GROUPNAME=${SELECT_USERNAME}
		    elif [[ ${SELECT_GROUP} =~ ^[[:digit:]]+$ && ${SELECT_GROUP} != ${SELECT_UID} ]]; then
		    # Группа имет цифровой GID и номер GID!=UID
			#ARG_GROUPADD_GID=" --gid ${SELECT_GROUP}"
			ARG_GROUPADD_GID="${SELECT_GROUP}"
			ARG_GROUPADD_GROUPNAME=${SELECT_USERNAME}
		    elif [[ ${SELECT_GROUP} =~ [[:alpha:]]+ ]]; then
		    # Группа имет буквенный GID
			ARG_GROUPADD_GROUPNAME=${SELECT_USERNAME}
		    else
		    # Если группа не имеет цифры и буквы
			unset SELECT_GROUP
		    fi 
		    [[ -n ${ARG_GROUPADD_GROUPNAME} ]] && exec_05_groupadd "GROUPADD[${ARG_GROUPADD_GROUPNAME}]=x:${ARG_GROUPADD_GID}"
		fi
		# Создаём пользователя
		if [[ -x ${ROOTFS}/usr/bin/useradd ]]; then
		    [[ -n ${SELECT_GECOS} ]] && ARG_SELECT_GECOS="--comment ${SELECT_GECOS}" || unset ARG_SELECT_GECOS
		    [[ -n ${SELECT_GROUP} ]] && ARG_SELECT_GROUP="--gid ${SELECT_GROUP}" || unset ARG_SELECT_GROUP
		    [[ -n ${SELECT_PASSWORD} ]] && ARG_SELECT_PASSWORD="--password ${SELECT_PASSWORD}" || unset ARG_SELECT_PASSWORD
		    ARG_SELECT_OPTIONAL="${SELECT_OPTIONAL}"
		    [[ ${SELECT_OPTIONAL} =~ ("-o"|"--non-unique") ]] && [[ -n ${ARG_SELECT_GROUP} ]] || { SELECT_OPTIONAL=${SELECT_OPTIONAL//-o/}; SELECT_OPTIONAL=${SELECT_OPTIONAL//--non-unique/}; }
		    [[ ${SELECT_OPTIONAL} =~ ("-M"|"--no-create-home") ]] || { [[ -d "${ROOTFS}${PATH_HOME}/${SELECT_USERNAME}" ]] || ARG_SELECT_OPTIONAL+=" --create-home"; }
		    [[ ${SELECT_OPTIONAL} =~ ("-N"|"--no-user-group") ]] || { [[ -z ${SELECT_GROUP} ]] && ARG_SELECT_OPTIONAL+=" --user-group"; }
		    ${CMD_CHROOT} /usr/bin/useradd ${ARG_SELECT_UID} ${ARG_SELECT_GROUP} ${ARG_SELECT_GECOS} ${ARG_SELECT_PASSWORD} ${ARG_SELECT_OPTIONAL} ${SELECT_USERNAME} #>/dev/null 2>&1
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		# busybox adduser
		    [[ -n ${SELECT_GECOS} ]] && ARG_SELECT_GECOS="-g ${SELECT_GECOS}" || unset ARG_SELECT_GECOS
		    [[ -n ${SELECT_GROUP} ]] && ARG_SELECT_GROUP="-G ${SELECT_GROUP}" || ARG_SELECT_GROUP="-G ${SELECT_USERNAME}"
		    [[ ${SELECT_OPTIONAL} =~ ("-M"|"--no-create-home") ]] && ARG_SELECT_OPTIONAL+=" -H"
		    ${CMD_CHROOT} /usr/bin/busybox adduser -D ${ARG_SELECT_UID} ${ARG_SELECT_GROUP} ${ARG_SELECT_GECOS} ${ARG_SELECT_OPTIONAL} ${SELECT_USERNAME} #>/dev/null 2>&1
		    # Задаём пароль пользователю
		    set_passwd "${SELECT_USERNAME}" "${SELECT_PASSWORD}"
		fi
		if [[ -x ${ROOTFS}/usr/bin/usermod ]]; then
		    # Добавляем пользователя в основную группу
		    #${CMD_CHROOT} /usr/bin/usermod -a -G ${SELECT_EXTRAGROUPS%*,} ${SELECT_USERNAME} #>/dev/null 2>&1
		    # Добавляем пользователя в дополнительные группы
		    ${CMD_CHROOT} /usr/bin/usermod -a -G ${SELECT_EXTRAGROUPS%*,} ${SELECT_USERNAME} #>/dev/null 2>&1
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		    true
		fi
		# Задаём параметры пароля пользователю /etc/shadow из USERSHADOW[user]. Только если запущено отдельно с параметром.
		[[ -n ${PARAM} && -n ${USERSHADOW[${SELECT_USERNAME}]} ]] && exec_04_usershadow "USERSHADOW[${SELECT_USERNAME}]=${USERSHADOW[${SELECT_USERNAME}]}"
		# Проверим права на домашний каталог пользователя совпадают с указанным польователем, если нет, то переназначим
		if [[ -d ${ROOTFS}${PATH_HOME}/${SELECT_USERNAME} && $(${CMD_CHROOT} /usr/bin/stat -c "%U:%G" ${PATH_HOME}/${SELECT_USERNAME}) != "${SELECT_USERNAME}:${SELECT_GROUP:-${SELECT_USERNAME}}" ]]; then
		    [[ -n ${ROOTFS} ]] && ARG_RECURSIVE="-R" || unset ARG_RECURSIVE
		    ${CMD_CHROOT} /usr/bin/chown -f ${ARG_RECURSIVE} "${SELECT_USERNAME}" ${PATH_HOME}/"${SELECT_USERNAME}"
		    ${CMD_CHROOT} /usr/bin/chown -f ${ARG_RECURSIVE} :"${SELECT_GROUP:-${SELECT_USERNAME}}" ${PATH_HOME}/"${SELECT_USERNAME}"
		fi
	    fi
	    # Создаём домашний каталог принудительно и копируем /etc/skel
	    if [[ ${UPDATEHOME,,} == @(yes|y|enable) \
	    || ${UPDATEHOME[${SELECT_USERNAME}],,} == @(yes|y|enable) ]]; then
	        create_home "${SELECT_USERNAME}" "${SELECT_GROUP}" force
	    fi
	done 3< <(printf "%s\n" "${!USERADD[@]}")
    elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then
	if [[ ${PARAM%%=*} =~ ^.*'['(.*)']' ]]; then
	# Удалим пользователей только тех кто содержиться в файле учетных данных ${FILE_ROOT_USERS}
	    SELECT_USERNAME=${BASH_REMATCH[1]}
	    delete_select_username(){
		local SELECT_USERNAME=$1
		if [[ -n ${SELECT_USERNAME} ]] && ${CMD_CHROOT} /usr/bin/getent passwd ${SELECT_USERNAME} &>/dev/null; then
		    ${CMD_CHROOT} /usr/bin/userdel --force ${SELECT_USERNAME}
		fi
		[[ -f ${FILE_ROOT_USERS} ]] && sed "/USERADD\[${SELECT_USERNAME}\]/d" -i "${FILE_ROOT_USERS}" 2>/dev/null
		${CMD_CHROOT} /usr/bin/ubconfig --noexecute remove [users] "USERSHADOW[${SELECT_USERNAME}]"
	    }
	    if [[ ${SELECT_USERNAME} == @("*"|"**"|"/"|"//") ]]; then
		[[ -f ${FILE_ROOT_USERS} ]] && while IFS= read -ru3 LINE_USERADD; do
		    [[ ${LINE_USERADD} =~ ^.*'['(.*)']' ]] && delete_select_username ${BASH_REMATCH[1]}
		done 3< <(grep -E "USERADD\[.*\]" ${FILE_ROOT_USERS} 2>/dev/null)
	    else
		delete_select_username ${SELECT_USERNAME}
	    fi
	fi
    fi
}

# Параметры пользователя системы /etc/shadow. Если пользователь существует, то без изменений
# $1    Команды set или remove с режимом, варианты: set=|set+=|set++=|set-=|set--=|remove
# $2    Для команды set=|set+=|set++= параметр со значением, пример: USERSHADOW[superadmin]=2023-01-01:0:99999:7::2025-01-01
#       Для команды set-=|set--=|remove параметр только с именем, пример: USERSHADOW[superadmin]
# null  Если отсутствует $@, то применяем из системной конфигурации USERSHADOW[*]

## USERSHADOW[user_name]='lastchanged:minday:maxday:warn:inactive:expire'
##   user_name          # Имя пользователя, обязательное поле
##   lastchanged        # Дата последнего изменения пароля. Указывается количество дней, исчисляется с 1 января 1970 года (дата эпохи). Возможно указать дату формата: YYYY-MM-DD
##   minday             # Минимальное количество дней действия пароля, прежде чем пароль пользователя может быть изменен. По умолчанию 0 означает отсутствие минимального срока действия парол
##   maxday             # Максимальное количество дней действия пароля после смены пароля пользователя. По умолчанию этот номер установлен на 99999
##   warn               # Количество дней предупреждения, в течение которого пользователь получает предупреждение о необходимости изменения пароля. По умолчанию 7
##   inactive           # Количество дней не активности пароля до отключения учетной записи пользователя. По умолчанию пустое
##   expire             # Дата, когда учетная запись была отключена. Указывается количество дней, исчисляется с 1 января 1970 года (дата эпохи). Возможно указать дату формата: YYYY-MM-DD
##   <null>             # Если один из параметров не задан, содержит пустое значение, то исходное значение не изменяется
##                      # Конвертировать кол-во дней от эпохи в понятную дату: date --date=@$(( DDDDD*(60*60*24) )); date --date=@EPOCH
## USERSHADOW[superadmin]=2023-01-01:0:99999:7::2025-01-01
## USERSHADOW[superadmin]=18009:0:120:7:14:
exec_04_usershadow(){
#echo "exec_04_usershadow"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local SELECT_USERNAME SELECT_LASTCHANGED SELECT_MINDAY SELECT_MAXDAY SELECT_WARN SELECT_INACTIVE SELECT_EXPIRE NULL
    local ARG_SELECT_LASTCHANGED ARG_SELECT_MINDAY ARG_SELECT_MAXDAY ARG_SELECT_WARN ARG_SELECT_INACTIVE ARG_SELECT_EXPIRE
    local PARAM="$@"
    if [[ -n ${PARAM} ]]; then
	local USERSHADOW
	declare -A USERSHADOW
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ ${#USERSHADOW[@]} != 0 ]]; then
        while IFS= read -ru3 SELECT_USERNAME; do
	    IFS=: read -r SELECT_LASTCHANGED SELECT_MINDAY SELECT_MAXDAY SELECT_WARN SELECT_INACTIVE SELECT_EXPIRE NULL <<< "${USERSHADOW[${SELECT_USERNAME}]}"
    	    # Получить из секунд от эпохи текущую дату: date -d @1705841503
    	    # Получить от эпохи количество дней: $(( $(date +%s)/(60*60*24) )). В дне 86400 секунд (60*60*24)
    	    #[[ ${SELECT_LASTCHANGED} =~ ^[0-9]{4,4}'-'[0-9]{1,2}'-'[0-9]{1,2}$ ]] && SELECT_LASTCHANGED_EPOH=$(date --date=${SELECT_LASTCHANGED} +"%s")
    	    #[[ -z ${SELECT_LASTCHANGED_EPOH} && -x /bin/busybox && ${SELECT_LASTCHANGED} =~ ^[0-9]{4,4}'.'[0-9]{1,2}'.'[0-9]{1,2}$ ]] && SELECT_LASTCHANGED_EPOH=$(busybox date --date="${SELECT_LASTCHANGED//./}0000" +"%s")
    	    #[[ -z ${SELECT_LASTCHANGED_EPOH} && -x ${ROOTFS}/usr/bin//date && ${SELECT_LASTCHANGED} =~ ^[0-9]{4,4}'.'[0-9]{1,2}'.'[0-9]{1,2}$ ]] && SELECT_LASTCHANGED_EPOH=$(${ROOTFS}/usr/bin/date --date="${SELECT_LASTCHANGED//./} 0000" +"%s")
    	    #[[ -n ${SELECT_LASTCHANGED_EPOH} ]] && SELECT_LASTCHANGED=$(( ${SELECT_LASTCHANGED_EPOH}/(60*60*24) ))
    	    #[[ ${SELECT_LASTCHANGED,,} == @(""|"x") || ${SELECT_LASTCHANGED} =~ ^[^0-9]*$ || ! ${SELECT_LASTCHANGED} =~ ^[0-9]{4,4}'-'[0-9]{1,2}'-'[0-9]{1,2}$ ]] && unset SELECT_LASTCHANGED
    	    [[ ${SELECT_LASTCHANGED} =~ (^[0-9]*$|^[0-9]{4,4}'-'[0-9]{1,2}'-'[0-9]{1,2}$) ]] || unset SELECT_LASTCHANGED
    	    [[ ${SELECT_MINDAY} =~ ^[0-9]*$ ]] || unset SELECT_MINDAY
    	    [[ ${SELECT_MAXDAY} =~ ^[0-9]*$ ]] || unset SELECT_MAXDAY
    	    [[ ${SELECT_WARN} =~ ^[0-9]*$ ]] || unset SELECT_WARN
    	    [[ ${SELECT_INACTIVE} =~ ^[0-9]*$ ]] || unset SELECT_INACTIVE
    	    #[[ ${SELECT_EXPIRE} =~ ^[0-9]{4,4}'-'[0-9]{1,2}'-'[0-9]{1,2}$ ]] && SELECT_EXPIRE_EPOH=$(date --date=${SELECT_EXPIRE} +"%s")
    	    #[[ -z ${SELECT_EXPIRE_EPOH} && -x /bin/busybox && ${SELECT_EXPIRE} =~ ^[0-9]{4,4}'.'[0-9]{1,2}'.'[0-9]{1,2}$ ]] && SELECT_EXPIRE_EPOH=$(busybox date --date="${SELECT_EXPIRE//./}0000" +"%s")
    	    #[[ -z ${SELECT_EXPIRE_EPOH} && -x ${ROOTFS}/usr/bin/date && ${SELECT_EXPIRE} =~ ^[0-9]{4,4}'.'[0-9]{1,2}'.'[0-9]{1,2}$ ]] && SELECT_EXPIRE_EPOH=$(${ROOTFS}/usr/bin/date --date="${SELECT_EXPIRE//./} 0000" +"%s")
    	    #[[ -n ${SELECT_EXPIRE_EPOH} ]] && SELECT_EXPIRE=$(( ${SELECT_EXPIRE_EPOH}/(60*60*24) ))
    	    [[ ${SELECT_EXPIRE} =~ (^[0-9]*$|^[0-9]{4,4}'-'[0-9]{1,2}'-'[0-9]{1,2}$) ]] || unset SELECT_EXPIRE
	    [[ -n ${SELECT_LASTCHANGED} ]] && ARG_SELECT_LASTCHANGED="--lastday ${SELECT_LASTCHANGED}" || unset ARG_SELECT_LASTCHANGED
	    [[ -n ${SELECT_MINDAY} ]] && ARG_SELECT_MINDAY="--mindays ${SELECT_MINDAY}" || unset ARG_SELECT_MINDAY
	    [[ -n ${SELECT_MAXDAY} ]] && ARG_SELECT_MAXDAY="--maxdays ${SELECT_MAXDAY}" || unset ARG_SELECT_MAXDAY
	    [[ -n ${SELECT_WARN} ]] && ARG_SELECT_WARN="--warndays ${SELECT_WARN}" || unset ARG_SELECT_WARN
	    [[ -n ${SELECT_INACTIVE} ]] && ARG_SELECT_INACTIVE="--inactive ${SELECT_INACTIVE}" || unset ARG_SELECT_INACTIVE
	    [[ -n ${SELECT_EXPIRE} ]] && ARG_SELECT_EXPIRE="--expiredate ${SELECT_EXPIRE}" || unset ARG_SELECT_EXPIRE
	    # Задаём параметры пароля пользователя
	    if [[ -x ${ROOTFS}/usr/bin/chage ]]; then
		${CMD_CHROOT} /usr/bin/chage ${ARG_SELECT_LASTCHANGED} ${ARG_SELECT_MINDAY} ${ARG_SELECT_MAXDAY} ${ARG_SELECT_WARN} ${ARG_SELECT_INACTIVE} ${ARG_SELECT_EXPIRE} "${SELECT_USERNAME}" #>/dev/null 2>&1
	    fi
	done 3< <(printf "%s\n" "${!USERSHADOW[@]}")
    elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then
	if [[ ${PARAM%%=*} =~ ^.*'['(.*)']' ]]; then
	    SELECT_USERNAME=${BASH_REMATCH[1]}
	    ${CMD_CHROOT} /usr/bin/chage --mindays 0 --maxdays 99999 --warndays 7 --inactive -1 --expiredate -1 ${SELECT_USERNAME}
	fi
    fi
}

# Функция добавление/удаление групп в систему. Если группа существует, то изменить под параметры
# $1    Команды set или remove с режимом, варианты: set=|set+=|set++=|set-=|set--=|remove
# $2    Для команды set=|set+=|set++= параметр со значением, пример: GROUPADD[g_department_1]=ob.ivanov,rv.smirnov
#       Для команды set-=|set--=|remove параметр только с именем, пример: GROUPADD[g_department_1]
# null  Если отсутствует $@, то применяем из системной конфигурации GROUPADD[*]

## GROUPADD[group_name]='group_users:gid:optional:password|x'
##   group_name		# Имя группы
##   group_users	# Пользователи группы, перечисление через запятую. Может быть пусто.
##   gid		# GID группы, если необходимо автоматически рассчитывать, то оставить пустым или 'x'
##   optional		# Дополнительные параметры, например: '--system --non-unique'
##     -o, --non-unique	# Разрешить создание групп с повторяющимися (не уникальными) GID
##     -r, --system	# Cоздавать системную группу
##   administrators     # Администраторы группы которые могут менять пароль группы и добавлять членов
##   password|x		# Хеш пароля группа, если выбрано 'x' или пусто, то группа без пароля
## GROUPADD[g_department_1]=ob.ivanov,rv.smirnov
## GROUPADD[g_department_2]='ob.ivanov,rv.smirnov:1001:x:superadmin:$6$E7stRhRS8fCKk7UU$Qoqw62AUaUa5uLIc2KC7WV3MUThhrR8kjXtCODmnKCzKe2zHu1/wmsiWBHZEIk/IQnk/aELQYbUK93OUtrwg60'
exec_05_groupadd(){
#echo "exec_05_groupadd"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local PARAM="$@"
    local SELECT_GROUP SELECT_USERS SELECT_GID SELECT_OPTIONAL SELECT_ADMINISTRATORS SELECT_PASSWORD NULL
    local ARG_SELECT_USERS ARG_SELECT_GID SELECT_OPTIONAL ARG_SELECT_PASSWORD ARG_NON_UNIQUE
    local DATA_FILE_PASSWD REAL_SELECT_USERS REAL_SELECT_ADMINISTRATORS
    if [[ -n ${PARAM} ]]; then
	local GROUPADD=
	declare -A GROUPADD
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ ${#GROUPADD[@]} != 0 ]]; then
        while IFS= read -ru3 SELECT_GROUP; do
	    # Добавить параметр в ${FILE_ROOT_USERS}=.users_credential и удалить параметр GROUPADD[.*] из '/etc/ublinux/users
	    if [[ -n ${PARAM} && -z ${ROOTFS} ]]; then
		if [[ -f ${FILE_ROOT_USERS} ]]; then
		    sed "/GROUPADD\[${SELECT_GROUP}\]=/d" -i "${FILE_ROOT_USERS}"
		    echo "GROUPADD[${SELECT_GROUP}]='${GROUPADD[${SELECT_GROUP}]}'" >> ${FILE_ROOT_USERS}
		fi
		[[ -f "${SYSCONF}/users" ]] && sed "/GROUPADD\[.*\]=/d" -i "${SYSCONF}/users"
	    fi
	    IFS=: read -r SELECT_USERS SELECT_GID SELECT_OPTIONAL SELECT_ADMINISTRATORS SELECT_PASSWORD NULL <<< "${GROUPADD[${SELECT_GROUP}]}"
	    [[ ${SELECT_USERS} == "x" ]] && unset SELECT_USERS
	    [[ ${SELECT_GID,,} == "x" || ${SELECT_GID} =~ ^[^0-9]*$ ]] && unset SELECT_GID
#echo "==> ${SELECT_GROUP}:${SELECT_USERS}:${SELECT_GID}:${SELECT_OPTIONAL}:${SELECT_ADMINISTRATORS}:${SELECT_PASSWORD}"
	    [[ ${SELECT_OPTIONAL} == "x" ]] && unset SELECT_OPTIONAL
	    [[ ${SELECT_PASSWORD} == @(""|"x") ]] && unset SELECT_PASSWORD
	    [[ ${SELECT_PASSWORD} != @(""|'!*'|'!'|'*') ]] && SELECT_PASSWORD=$(return_hash_password hash ${HASHPASSWD} ${SELECT_PASSWORD})    	    
	    # Поиск по имени в шаблонах пользователей/групп systemd
	    [[ $(cat ${ROOTFS}/usr/share/ublinux-sysusers/*.sysusers) =~ ($'\n'|^)+'g'[[:blank:]]+"${SELECT_GROUP}"[[:blank:]]+([[:digit:]]+)[^$'\n']*($'\n'|$)+ ]] && FINDGROUP_ID=${BASH_REMATCH[2]} || unset FINDGROUP_ID
	    # Поиск по GID в шаблонах пользователей/групп systemd
	    [[ -z ${FINDGROUP_ID} ]] \
	    && [[ $(cat ${ROOTFS}/usr/share/ublinux-sysusers/*.sysusers) =~ ($'\n'|^)+'g'[[:blank:]]+([^$'\n']+)[[:blank:]]+"${SELECT_GROUP}"[^$'\n']*($'\n'|$)+ ]] && FINDGROUP_NAME=${BASH_REMATCH[2]} || unset FINDGROUP_NAME
	    [[ -n ${FINDGROUP_ID} ]] && SELECT_GID="${FINDGROUP_ID}"
	    [[ -n ${FINDGROUP_NAME} ]] && SELECT_GID="${FINDGROUP_NAME}"
	    DATA_FILE_GROUP=$(cat ${FILE_GROUP} 2>/dev/null)
	    # Добавляем аргументы к опциям, при условии что такого GID не существует
	    [[ -n ${SELECT_GID} ]] && [[ ! ${DATA_FILE_GROUP} =~ :${SELECT_GID}: ]] && ARG_SELECT_GID="--gid ${SELECT_GID}" || unset ARG_SELECT_GID
	    #[[ -n ${SELECT_USERS} ]] && ARG_SELECT_USERS="--users ${SELECT_USERS}" || unset ARG_SELECT_USERS
	    [[ -n ${SELECT_PASSWORD} ]] && ARG_SELECT_PASSWORD="--password ${SELECT_PASSWORD}" || unset ARG_SELECT_PASSWORD
	    [[ ${SELECT_OPTIONAL} =~ ("-o"|"--non-unique") ]] && [[ -n ${ARG_SELECT_GID} ]] || { SELECT_OPTIONAL=${SELECT_OPTIONAL//-o/}; SELECT_OPTIONAL=${SELECT_OPTIONAL//--non-unique/}; }
	    # Создаём группу, если создана то изменяем под установленные параметры
	    if [[ ! ${DATA_FILE_GROUP} =~ ($'\n'|^)+${SELECT_GROUP}: ]]; then
		if [[ -x ${ROOTFS}/usr/bin/groupadd ]]; then
		    #${CMD_CHROOT} /usr/bin/groupadd --force ${ARG_SELECT_USERS} ${ARG_SELECT_GID} ${SELECT_OPTIONAL} ${ARG_SELECT_PASSWORD} ${SELECT_GROUP}
		    ${CMD_CHROOT} /usr/bin/groupadd --force ${ARG_SELECT_GID} ${SELECT_OPTIONAL} ${ARG_SELECT_PASSWORD} ${SELECT_GROUP}
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		    true    
		    # Задаём пароль группе
		    # set_gpasswd "${SELECT_GROUP}" "${SELECT_PASSWORD}"
		fi
	    elif [[ -n ${ARG_SELECT_GID} ]]; then
		if [[ -x ${ROOTFS}/usr/bin/groupmod ]]; then
		    # т.к. groupmod принимет не все возможные аргументы совместимы с groupadd, то фильтруем
		    [[ ${SELECT_OPTIONAL} =~ ("--non-unique"|"-o") ]] && [[ -n ${ARG_SELECT_GID} ]] && ARG_NON_UNIQUE="--non-unique" || unset ARG_NON_UNIQUE
		    [[ -n ${SELECT_PASSWORD} ]] && ARG_SELECT_PASSWORD="--password ${SELECT_PASSWORD}" || unset ARG_SELECT_PASSWORD
		    #${CMD_CHROOT} /usr/bin/groupmod --append ${ARG_SELECT_USERS} ${ARG_SELECT_GID} ${ARG_NON_UNIQUE} ${ARG_SELECT_PASSWORD} ${SELECT_GROUP}
		    ${CMD_CHROOT} /usr/bin/groupmod ${ARG_SELECT_GID} ${ARG_NON_UNIQUE} ${ARG_SELECT_PASSWORD} ${SELECT_GROUP}
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
		    true    
		    # Задаём пароль группе
		    # set_gpasswd "${SELECT_GROUP}" "${SELECT_PASSWORD}"
		fi
	    fi
	    DATA_FILE_PASSWD=$(cat ${FILE_PASSWD} 2>/dev/null)
	    unset REAL_SELECT_USERS REAL_SELECT_ADMINISTRATORS
	    # Если пользователь не существуют то убрать из списка SELECT_USERS
	    while IFS= read -r READ_SELECT_USERS; do
	    	    [[ ${DATA_FILE_PASSWD} =~ ($'\n'|^)+${READ_SELECT_USERS}: ]] && REAL_SELECT_USERS+=",${READ_SELECT_USERS}"
	    done <<< ${SELECT_USERS//,/$'\n'}
	    [[ ${REAL_SELECT_USERS:0:1} == "," ]] && REAL_SELECT_USERS=${REAL_SELECT_USERS:1}
	    [[ ${REAL_SELECT_USERS} != "" ]] && SELECT_USERS=${REAL_SELECT_USERS} || unset SELECT_USERS
	    # Если пользователь не существуют то убрать из списка REAL_SELECT_ADMINISTRATORS
	    while IFS= read -r READ_SELECT_ADMINISTRATORS; do
	    	    [[ ${DATA_FILE_PASSWD} =~ ($'\n'|^)+${READ_SELECT_ADMINISTRATORS}: ]] && REAL_SELECT_ADMINISTRATORS+=",${READ_SELECT_ADMINISTRATORS}"
	    done <<< ${SELECT_ADMINISTRATORS//,/$'\n'}
	    [[ ${REAL_SELECT_ADMINISTRATORS:0:1} == "," ]] && REAL_SELECT_ADMINISTRATORS=${REAL_SELECT_ADMINISTRATORS:1}
	    [[ ${REAL_SELECT_ADMINISTRATORS} != "" ]] && SELECT_ADMINISTRATORS=${REAL_SELECT_ADMINISTRATORS} || unset SELECT_ADMINISTRATORS
	    # Добавляем пользователей в группу, т.к. groupadd не добавил пользователей в /etc/shadow
	    if [[ -n ${SELECT_USERS} || -n ${SELECT_ADMINISTRATORS} ]]; then
		if [[ -x ${ROOTFS}/usr/bin/gpasswd ]]; then
		    [[ -n ${SELECT_USERS} ]] && ARG_SELECT_USERS="-M ${SELECT_USERS}" || unset ARG_SELECT_USERS
		    [[ -n ${SELECT_ADMINISTRATORS} ]] && ARG_SELECT_ADMINISTRATORS="-A ${SELECT_ADMINISTRATORS}" || unset ARG_SELECT_ADMINISTRATORS
	    	    ${CMD_CHROOT} /usr/bin/gpasswd ${ARG_SELECT_ADMINISTRATORS} ${ARG_SELECT_USERS} ${SELECT_GROUP} #>/dev/null 2>&1
		elif [[ -x ${ROOTFS}/usr/bin/busybox ]]; then
	    	    true
		fi
	    fi
	done 3< <(printf "%s\n" "${!GROUPADD[@]}")
	# Выполнить отдельно только если указан параметр функции
    elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then
	if [[ ${PARAM%%=*} =~ ^.*'['(.*)']' ]]; then
	    SELECT_GROUP=${BASH_REMATCH[1]}
	    delete_select_group(){
		local SELECT_GROUP=$1
		if [[ -n ${SELECT_GROUP} ]] && ${CMD_CHROOT} /usr/bin/getent group ${SELECT_GROUP} &>/dev/null; then
		    ${CMD_CHROOT} /usr/bin/groupdel --force ${SELECT_GROUP}
		fi
		[[ -f ${FILE_ROOT_USERS} ]] && sed "/GROUPADD\[${SELECT_GROUP}\]/d" -i "${FILE_ROOT_USERS}" 2>/dev/null
	    }
	    if [[ ${SELECT_GROUP} == @("*"|"**"|"/"|"//") ]]; then
		[[ -f ${FILE_ROOT_USERS} ]] && while IFS= read -ru3 LINE_GROUPADD; do
		    [[ ${LINE_GROUPADD} =~ ^.*'['(.*)']' ]] && delete_select_group ${BASH_REMATCH[1]}
		done 3< <(grep -E "GROUPADD\[.*\]" ${FILE_ROOT_USERS} 2>/dev/null)
	    else
		delete_select_group ${SELECT_GROUP}
	    fi
	fi
    fi
}

# Autodetect firststart
# Если пароли по умолчанию, то firststart
exec_99_firststart(){
#echo "exec_99_firststart"
    [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && COMMAND=$1 && shift
    [[ -n ${COMMAND} ]] || COMMAND="set="
    local PARAM="$@"
    if [[ -n ${PARAM} ]]; then
	local FIRSTSTART=
	[[ ${PARAM%%=*} =~ [!\$%\&()*+,/\;\<\=\>?\^\{|\}~] ]] || eval "${PARAM%%=*}=\${PARAM#*=}"
    fi
    if [[ -n ${ROOTFS} ]]; then
	if grep -q "^root:${DEFAULTROOTPASSWD}:" ${ROOTFS}/etc/shadow \
	&& grep -q "^$(grep ".*:x:${ADMUID}:" ${ROOTFS}/etc/passwd | cut -d: -f1):${DEFAULTPASSWD}:" ${ROOTFS}/etc/shadow; then
	    grep -q "^FIRSTSTART$" ${SYSCONF}/config 2>/dev/null || echo "FIRSTSTART=yes" >> ${SYSCONF}/config
	fi
    elif [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ -n ${FIRSTSTART} ]]; then
	[[ $(cat ${SYSCONF}/config 2>/dev/null) =~ ($'\n'|^)+'FIRSTSTART=' ]] || echo "FIRSTSTART=yes" >> ${SYSCONF}/config
    elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then
	sed "/FIRSTSTART=/d" -i ${SYSCONF}/config
    fi
}


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