#!/usr/bin/env bash # # Author: Dmitry Razumov # Copyright (c) 2021-2025 UBLinux # # Extended pattern matching: https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html#Pattern-Matching shopt -s extglob ENABLED=yes [[ ${ENABLED} == yes ]] || exit 0 SOURCE=/usr/lib/ublinux/functions; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null SOURCE=/usr/lib/ublinux/default; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null SOURCE=${SYSCONF}/config; [[ -f ${SOURCE} ]] && . ${SOURCE} 2>/dev/null SOURCE=${SYSCONF}/logging; [ -f ${SOURCE} ] && . ${SOURCE} 2>/dev/null SOURCE=${SYSCONF}/network; [ -f ${SOURCE} ] && . ${SOURCE} 2>/dev/null ## Назначение модулей: ## account подтвердить личность, проверив учетные данные, такие как пароль, ключ, токен и другие ## auth проверить авторизацию для таких действий, как разрешения, ограничения и т. д. ## password обновить учетные данные ## session распределять ресурсы во время входа в систему, такие как личные данные, лимиты и другие. ## ## Выполнение из /etc/pam.d/system-login ## Тип модуля: session ## Глобальные переменные: ## PAM_RHOST Удалённый хост ## PAM_RUSER Удалённый пользователь ## PAM_SERVICE Сервис выполняющий вход /etc/pam.d/service_name ## PAM_TTY Консоль, может быть как "/dev/tty2" так и ":0" ## PAM_USER Текущий пользователь ## PAM_TYPE Тип сессии, возможные значения: account, auth, password, open_session, close_session ## ## PAM_RHOST= PAM_RUSER= PAM_SERVICE=login PAM_TTY=/dev/tty2 PAM_USER=superadmin PAM_TYPE=open_session ## PAM_RHOST= PAM_RUSER= PAM_SERVICE=login PAM_TTY=/dev/tty2 PAM_USER=superadmin PAM_TYPE=close_session ## PAM_RHOST= PAM_RUSER= PAM_SERVICE=lightdm-autologin PAM_TTY=:0 PAM_USER=superadmin PAM_TYPE=open_session ## PAM_RHOST= PAM_RUSER= PAM_SERVICE=systemd-user PAM_TTY= PAM_USER=lightdm PAM_TYPE=open_session ## PAM_RHOST= PAM_RUSER= PAM_SERVICE=systemd-user PAM_TTY= PAM_USER=user-2 PAM_TYPE=open_session ## PAM_RHOST= PAM_RUSER= PAM_SERVICE=lightdm PAM_TTY=:0 PAM_USER=user-2 PAM_TYPE=open_session ## PAM_RHOST=1.2.3.4 PAM_RUSER= PAM_SERVICE=sshd PAM_TTY=ssh PAM_USER=user-2 PAM_TYPE=open_session if [[ ${PAM_TYPE} == "open_session" && -n ${PAM_USER} ]]; then # Выполнение функции вызвано используя PAM, получаем имя пользователя вызвавшего PAM, будем пременять только для пользователя SELECT_USER="${PAM_USER}" fi ## Вывод уведомления на рабочий стол полученных от лога journald ## JOURNALD_NOTIFY[:::]=;; ## # Тип фильтра ## u # Фильтр по юнитам системным или pattern ## uu # Фильтр (user-unit) по юнитам пользователя или pattern ## t # Фильтр (identifier) по идентификатору или pattern ## f # Фильтр (facility) по типу объекта , строгое значение ## # Значения для типа фильтра ## # Если type=u то имя юнита системы или pattern ## # Если type=uu то имя юнита пользователя или pattern ## # Если type=t то имя идентификатора или pattern. Вывести доступные: journalctl -F SYSLOG_IDENTIFIER ## # Если type=f то применимы варианты facility: ## 0 | kern # 0: kern (Kernel messages) ## 1 | user # 1: user (User-level messages) ## 2 | mail # 2: mail (Mail system) ## 3 | daemon # 3: daemon (System daemons) ## 4 | auth # 4: auth (Security/authorization messages) ## 5 | syslog # 5: syslog (Messages generated internally by syslogd) ## 6 | lpr # 6: lpr (Line printer subsystem (archaic subsystem)) ## 7 | news # 7: news (Network news subsystem (archaic subsystem)) ## 8 | uucp # 8: uucp (UUCP subsystem (archaic subsystem)) ## 9 # 9: - (Clock daemon systemd-timesyncd) ## 10| authpriv # 10: authpriv (Security/authorization messages) ## 11| ftp # 11: ftp (FTP daemon) ## 12 # 12: - (NTP subsystem) ## 13 # 13: - (Log audit) ## 14 # 14: - (Log alert) ## 15| cron # 15: cron (Scheduling daemon) ## 16| local0 # 16: local0 (Local use 0 (local0)) ## 17| local1 # 17: local1 (Local use 1 (local1)) ## 18| local2 # 18: local2 (Local use 2 (local2)) ## 19| local3 # 19: local3 (Local use 3 (local3)) ## 20| local4 # 20: local4 (Local use 4 (local4)) ## 21| local5 # 21: local5 (Local use 5 (local5)) ## 22| local6 # 22: local6 (Local use 6 (local6)) ## 23| local7 # 23: local7 (Local use 7 (local7)) ## # Фильтр по приоритету 0:emerg 1:alert 2:crit 3:err 4:warning 5:notice 6:info 7:debug ## 0 | emerg # 0: emergency (неработоспособность системы) ## 1 | alert # 1: alerts (предупреждения, требующие немедленного вмешательства) ## 2 | crit # 2: critical (критическое состояние) ## 3 | err # 3: errors (ошибки) ## 4 | warning # 4: warning (предупреждения) ## 5 | notice # 5: notice (уведомления) ## 6 | info # 6: info (информационные сообщения) ## 7 | debug # 7: debug (отладочные сообщения) ## # Произвольные дополнительные опции фильтра пользователя для journald ## # Фильтр сообщения ## @ # Фильтр отключен, выводить все сообщения ## ip@conflict # Фильтровать сообщения о конфликте IP адреса ## ip@manager-connecting # Фильтровать сообщения NetworkManager о соединении ## ip@manager-disconnecting # Фильтровать сообщения NetworkManager об отсоединении ## ip # Фильтровать сообщения NetworkManager ## # Шаблон обработки сообщения, произвольное выражение вырезки для команды sed ## JOURNALD_NOTIFY[u:NetworkManager:warning]=@ ## JOURNALD_NOTIFY[u:NetworkManager:warning]=ip@conflict ## JOURNALD_NOTIFY[u:*Network*:warning]=@ ## JOURNALD_NOTIFY[uu:NetworkManager:warning]=@ ## JOURNALD_NOTIFY[t:*Network*:warning]=@ ## JOURNALD_NOTIFY[f:daemon:warning]=@ ## JOURNALD_NOTIFY[u:NetworkManager:warning]=@ ## JOURNALD_NOTIFY[u:NetworkManager]=ip@manager-disconnecting,ip@manager-connecting #1749316860.013993 NetworkManager[34283]: [1749316860.0139] device (ens192): IP address 192.168.7.121 cannot be configured because it is already in use in the network by host 00:0C:29:80:F2:5E #1749316861.486311 NetworkManager[34283]: [1749316861.4862] device (ens192): Activation: failed for connection 'Проводное подключение 1' ## echo "$(printf 'journalctl'; printf ' -u %s' $(journalctl -q -F UNIT | grep -E '^Network'))" ## echo "$(printf 'journalctl'; printf ' -t %s' $(journalctl -q -F SYSLOG_IDENTIFIER | grep -E '^gnome'))" ## journalctl -t gnome-system-monitor.desktop -t gnome-shell -t gnome-keyring-daemon -t gnome-session-binary -t gnome-session ## TODO ## 1. type=uu требует доработки, т.к. запуск от рута и будут видны юниты только рута, а нужно от вошедшего пользователя. Поможет PAM pamsession расположение от которого запускается утилита ## Пользователь запустивший: ${SELECT_USER} ## 2. Рассмотреть запуск не через setsid, а через создание сервиса. Возможно и не нужно. exec_01_journald_notify(){ [[ $1 == @("set="|"set+="|"set++="|"set-="|"set--="|"remove") ]] && local COMMAND=$1 && shift [[ -n ${COMMAND} ]] || local COMMAND="set=" [[ $(declare -p JOURNALD_NOTIFY 2>/dev/null) =~ ^"declare -A" ]] || declare -gA JOURNALD_NOTIFY local PARAM="$@" if [[ -n ${PARAM} ]]; then local JOURNALD_NOTIFY= declare -A JOURNALD_NOTIFY=() [[ ${PARAM} =~ ^[[:alnum:]_]+("="|"[".*"]=") ]] && eval "${PARAM%%=*}=\${PARAM#*=}" fi if [[ ${COMMAND} == @("set="|"set+="|"set++=") ]] && [[ ${#JOURNALD_NOTIFY[@]} -ne 0 ]]; then local TYPE= TYPE_VOLUE= PRIORITY= OPTIONS= FILTER= for SELECT_JOURNALD_NOTIFY in "${!JOURNALD_NOTIFY[@]}"; do IFS=: read -r TYPE TYPE_VOLUE PRIORITY OPTIONS NULL <<< ${SELECT_JOURNALD_NOTIFY} [[ -n ${TYPE} && -n ${TYPE_VOLUE} ]] || continue case "${TYPE,,}" in "u") TYPE="--unit" ;; "uu") TYPE="--user-unit" ;; "t") TYPE="--identifier" ;; "f") TYPE="--facility" ;; *) continue ;; esac [[ ${TYPE} == "--facility" ]] && { [[ ${TYPE_VOLUE,,} == @(0|kern|1|user|2|mail|3|daemon|4|auth|5|syslog|6|lpr|7|news|8|uucp|9|10|authpriv|11|ftp|12|13|14|15|cron|16|local0|17|local1|18|local2|19|local3|20|local4|21|local5|22|local6|23|local7) ]] || continue; } [[ -n ${PRIORITY} && ${PRIORITY} == @(0|emerg|1|alert|2|crit|3|err|4|warning|5|notice|6|info|7|debug) ]] && PRIORITY="--priority ${PRIORITY}" FILTER=${JOURNALD_NOTIFY[${SELECT_JOURNALD_NOTIFY}]} pkill -f "/usr/lib/ublinux/rc.pamsession.d/02-journald-notify ubconfig_journald_notify_live ${TYPE} ${TYPE_VOLUE} ${PRIORITY} ${OPTIONS} ${FILTER}" setsid -f /usr/lib/ublinux/rc.pamsession.d/02-journald-notify ubconfig_journald_notify_live "${TYPE}" "${TYPE_VOLUE}" "${PRIORITY}" "${OPTIONS}" "${FILTER}" if [[ ${FILTER} =~ (^|,)"ip@conflict"(,|$) ]]; then # Включить таймаут обнаружения конфликтов IP на всех сетевых интерфейсах [[ ${NETWORK[all@connmod]} =~ "ipv4.dad-timeout -1" ]] || ubconfig --quiet --target system set [network] NETWORK[all@connmod]+=" ipv4.dad-timeout -1" fi done elif [[ ${COMMAND} == @("set-="|"set--="|"remove") ]]; then if [[ ${PARAM%%=*} =~ ^.*'['(.*)']' && ${BASH_REMATCH[1]} == @("*"|"**"|"/"|"//") ]]; then PARAM_VALUE="${PARAM#*=}" JOURNALD_NOTIFY+="${PARAM_VALUE// /,}" fi local TYPE= TYPE_VOLUE= PRIORITY= OPTIONS= FILTER= for SELECT_JOURNALD_NOTIFY in "${!JOURNALD_NOTIFY[@]}"; do IFS=: read -r TYPE TYPE_VOLUE PRIORITY OPTIONS NULL <<< ${SELECT_JOURNALD_NOTIFY} [[ -n ${TYPE} && -n ${TYPE_VOLUE} ]] || continue case "${TYPE,,}" in "u") TYPE="--unit" ;; "uu") TYPE="--user-unit" ;; "t") TYPE="--identifier" ;; "f") TYPE="--facility" ;; *) continue ;; esac [[ ${TYPE} == "--facility" ]] && { [[ ${TYPE_VOLUE,,} == @(0|kern|1|user|2|mail|3|daemon|4|auth|5|syslog|6|lpr|7|news|8|uucp|9|10|authpriv|11|ftp|12|13|14|15|cron|16|local0|17|local1|18|local2|19|local3|20|local4|21|local5|22|local6|23|local7) ]] || continue; } [[ -n ${PRIORITY} && ${PRIORITY} == @(0|emerg|1|alert|2|crit|3|err|4|warning|5|notice|6|info|7|debug) ]] && PRIORITY="--priority ${PRIORITY}" FILTER=${JOURNALD_NOTIFY[${SELECT_JOURNALD_NOTIFY}]} pkill -f "/usr/lib/ublinux/rc.pamsession.d/02-journald-notify ubconfig_journald_notify_live ${TYPE} ${TYPE_VOLUE} ${PRIORITY} ${OPTIONS} ${FILTER}" if [[ ${FILTER} =~ (^|,)"ip@conflict"(,|$) ]]; then # Выключить таймаут обнаружения конфликтов IP на всех сетевых интерфейсах [[ ${NETWORK[all@connmod]} =~ "ipv4.dad-timeout -1" ]] && ubconfig --quiet --target system set [network] NETWORK[all@connmod]-="ipv4.dad-timeout -1" fi done fi } ubconfig_journald_notify_live(){ local TYPE=$1 TYPE_VOLUE=$2 PRIORITY=$3 OPTIONS=$4 FILTER=$5 NOTIFY_ICON="--icon dialog-information --urgency=normal" init(){ export TEXTDOMAIN="ublinux-init-${0##*/}" export TEXTDOMAINDIR="${PATH_ROOT}/usr/share/locale" } i18n(){ local KEY="$1" shift printf "$(gettext -s "${KEY}")" "$@" } show_notify(){ local NO_NOTIFY= while IFS= read -ru3 SELECT_FILTER; do local NOTIFY_MESSAGE_SHOW= case "${SELECT_FILTER}" in "@") # Без фильтра true ;; "ip@conflict") # Фильтр NetworkManager.service с переводом # Убираем первых два блока тип сообщеия и дату, оставляем только: #device (ens192): IP address 192.168.7.121 cannot be configured because it is already in use in the network by host 00:0C:29:80:F2:5E FILTER_SED="s/^[^[:blank:]]+[[:blank:]]+[^[:blank:]]*[[:blank:]]+(.*)/\1/p" NOTIFY_MESSAGE_TRIM=$(sed -En "${FILTER_SED}" <<< ${NOTIFY_MESSAGE}) [[ ${NOTIFY_MESSAGE_TRIM} =~ ^"device ("([[:alnum:]]+)"): IP address "([[:digit:].]+)" cannot be configured because it is already in use in the network by host "([[:alnum:]:]+)$ ]] \ && NOTIFY_MESSAGE_SHOW=$(i18n "device (%s): IP address %s cannot be configured because it is already in use in the network by host %s" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}") #" ;; "ip@manager-connecting") # Фильтр NetworkManager.service с переводом # Убираем первых два блока тип сообщеия и дату FILTER_SED="s/^[^[:blank:]]+[[:blank:]]+[^[:blank:]]*[[:blank:]]+(.*)/\1/p" NOTIFY_MESSAGE_TRIM=$(sed -En "${FILTER_SED}" <<< ${NOTIFY_MESSAGE}) [[ ${NOTIFY_MESSAGE_TRIM} =~ ^"manager: NetworkManager state is now CONNECTING"$ ]] \ && NOTIFY_MESSAGE_SHOW=$(i18n "manager: NetworkManager state is now CONNECTING") ;; "ip@manager-disconnecting") # Фильтр NetworkManager.service с переводом # Убираем первых два блока тип сообщеия и дату FILTER_SED="s/^[^[:blank:]]+[[:blank:]]+[^[:blank:]]*[[:blank:]]+(.*)/\1/p" NOTIFY_MESSAGE_TRIM=$(sed -En "${FILTER_SED}" <<< ${NOTIFY_MESSAGE}) [[ ${NOTIFY_MESSAGE_TRIM} =~ ^"manager: NetworkManager state is now DISCONNECTING"$ ]] \ && NOTIFY_MESSAGE_SHOW=$(i18n "manager: NetworkManager state is now DISCONNECTING") ;; "ip") # Фильтр NetworkManager.service # Убираем первых два блока тип сообщеия и дату, оставляем только: FILTER_SED="s/^[^[:blank:]]+[[:blank:]]+[^[:blank:]]*[[:blank:]]+(.*)/\1/p" NOTIFY_MESSAGE_SHOW=$(sed -En "${FILTER_SED}" <<< ${NOTIFY_MESSAGE}) ;; *) # Фильтр пользователя FILTER_SED="${SELECT_FILTER}" NOTIFY_MESSAGE_SHOW=$(sed -En "${FILTER_SED}" <<< ${NOTIFY_MESSAGE}) ;; esac #[[ -n ${NOTIFY_MESSAGE_SHOW} ]] && echo "::${NOTIFY_ICON}::${NOTIFY_TIMESTAMP}::${NOTIFY_MESSAGE_SHOW}::" [[ -n ${NOTIFY_MESSAGE_SHOW} ]] && notify_send --app-name "$(i18n "UBLinux notify from Journald")" ${NOTIFY_ICON} "$(i18n "New notify from %s" "${NOTIFY_SYSTEMD_UNIT}")" "${NOTIFY_TIMESTAMP}\n\n${NOTIFY_MESSAGE_SHOW}" & #[[ -n ${NOTIFY_MESSAGE_SHOW} ]] && notify-send --app-name "UBConfig notify from Journald" ${NOTIFY_ICON} "New notify from UBConfig" "${NOTIFY_TIMESTAMP} ${NOTIFY_SYSTEMD_UNIT}\n\ndevice (ens192): IP address 192.168.7.121 cannot be configured because it is already in use in the network by host 00:0C:29:80:F2:5E" done 3<<< ${FILTER//,/$'\n'} } init journalctl ${TYPE} "${TYPE_VOLUE}" ${PRIORITY} --no-hostname --lines 1 --output export --follow ${OPTIONS} | while IFS= read SELECT_JOURNAL_ITEM; do [[ ${SELECT_JOURNAL_ITEM} =~ ($'\n'|^)+"PRIORITY="([^$'\n']*)($'\n'|$)+ ]] && case ${BASH_REMATCH[2]} in 0|emerg) NOTIFY_ICON="--icon dialog-error --urgency=critical" ;; 1|alert) NOTIFY_ICON="--icon dialog-error --urgency=critical" ;; 2|crit) NOTIFY_ICON="--icon dialog-error --urgency=critical" ;; 3|err) NOTIFY_ICON="--icon dialog-error --urgency=normal" ;; 4|warning) NOTIFY_ICON="--icon dialog-warning --urgency=normal" ;; 5|notice) NOTIFY_ICON="--icon dialog-information --urgency=low" ;; 6|info) NOTIFY_ICON="--icon dialog-information --urgency=low" ;; 7|debug) NOTIFY_ICON="--icon dialog-information --urgency=low" ;; esac [[ ${SELECT_JOURNAL_ITEM} =~ ($'\n'|^)+"__REALTIME_TIMESTAMP="([^$'\n']*)($'\n'|$)+ ]] && NOTIFY_TIMESTAMP=$(date '+%d.%m.%Y %H:%M:%S' -d @${BASH_REMATCH[2]:0:10}) [[ ${SELECT_JOURNAL_ITEM} =~ ($'\n'|^)+"_SYSTEMD_UNIT="([^$'\n']*)($'\n'|$)+ ]] && NOTIFY_SYSTEMD_UNIT="${BASH_REMATCH[2]}" [[ ${SELECT_JOURNAL_ITEM} =~ ($'\n'|^)+"MESSAGE="([^$'\n']*)($'\n'|$)+ ]] && NOTIFY_MESSAGE="${BASH_REMATCH[2]}" && show_notify done } ################ ##### 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 true