You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ublinux-init/ublinux/rc.pamsession.d/02-journald-notify

280 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
#
# Author: Dmitry Razumov <asmeron@ublinux.com>
# Copyright (c) 2021-2025 UBLinux <support@ublinux.com>
#
# 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[<type>:<type_value>:<priority>:<options>]=<filter>;<filter>;<filter>
## <type> # Тип фильтра
## u # Фильтр по юнитам системным или pattern
## uu # Фильтр (user-unit) по юнитам пользователя или pattern
## t # Фильтр (identifier) по идентификатору <SYSLOG_IDENTIFIER> или pattern
## f # Фильтр (facility) по типу объекта <SYSLOG>, строгое значение
## <type_value> # Значения для типа фильтра
## <unit> # Если type=u то имя юнита системы или pattern
## <user-unit> # Если type=uu то имя юнита пользователя или pattern
## <SYSLOG_IDENTIFIER> # Если 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))
## <priority> # Фильтр по приоритету 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 (отладочные сообщения)
## <options> # Произвольные дополнительные опции фильтра пользователя для journald
## <filter> # Фильтр сообщения
## @ # Фильтр отключен, выводить все сообщения
## ip@conflict # Фильтровать сообщения о конфликте IP адреса
## ip@manager-connecting # Фильтровать сообщения NetworkManager о соединении
## ip@manager-disconnecting # Фильтровать сообщения NetworkManager об отсоединении
## ip # Фильтровать сообщения NetworkManager
## <regexp_message> # Шаблон обработки сообщения, произвольное выражение вырезки для команды 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]: <warn> [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]: <warn> [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}")" "<b>${NOTIFY_TIMESTAMP}</b>\n\n${NOTIFY_MESSAGE_SHOW}" &
#[[ -n ${NOTIFY_MESSAGE_SHOW} ]] && notify-send --app-name "UBConfig notify from Journald" ${NOTIFY_ICON} "New notify from UBConfig" "<b>${NOTIFY_TIMESTAMP} ${NOTIFY_SYSTEMD_UNIT}</b>\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