我试图在登录时通过PAM发送通知,但我不知道如何将其发送给正在登录的用户。
我将我的PAM配置为每次用户登录时都执行一个脚本。问题是,如果有任何登录尝试,我需要发送一个通知(这是我试图添加的一个更大的安全问题的一部分,在登录失败时,我的笔记本电脑会用网络摄像头拍照,并在我再次登录时通知我,因为我的同学出于某种原因喜欢尝试猜测我的密码)。问题是,我的.sh文件中发送用户通知的行将其发送到root,因为这是执行脚本的"用户",我希望我的脚本将通知发送给我的当前用户(称为"andreas"),但我在弄清楚这一点时遇到了问题。
以下是我添加到PAM文件系统登录末尾的行:
auth [default=ignore] pam_exec.so /etc/lockCam/call.sh
这是call.sh文件:
#!/bin/sh
/etc/lockCam/notifier.sh &
我之所以调用另一个文件,是因为我希望它在登录过程继续时在后台运行,这样登录过程就不会减慢登录速度
以下是随后执行的脚本:
#!/bin/sh
#sleep 10s
echo -e "foo" > "/etc/lockCam/test"
#This line is simply to make sure that i know that my script was executed
newLogins=`sed -n '3 p' /etc/lockCam/lockdata`
if [ $newLogins -gt 0 ]
then
su andreas -c ' notify-send --urgency=critical --expire-time=6000 "Someone tried to log in!" "$newLogins new lockCam images!" && exit'
callsInRow=`sed -n '2 p' /etc/lockCam/lockdata`
crntS=$(date "+%S")
crntS=${crntS#0}
crntM=$(date "+%M")
crntM=${crntM#0}
crntH=$(date "+%H")
crntH=${crntH#0}
((crntTime = $crntH * 60 * 60 + $crntM * 60 + $crntS ))
#This whole process is absolutely stupid but i cant figure out a better way to make sure none of the integers are called "01" or something like that, which would trigger an error
echo -e "$crntTimen$callsInRown0" > "/etc/lockCam/lockdata"
fi
exit 0
这就是我认为我的错误所在,"su andreas-c…"这一行很可能格式错误,或者我做错了其他事情,除了通知没有出现之外,所有操作都在登录时执行。如果我在登录时从终端执行脚本,也没有通知,除非我删除"su andreas-c"部分并简单地执行"notify send…",但这不会在我登录时发出通知,我认为这是因为通知是发送给根用户的,而不是"andreas"。
我认为您的su需要传递给桌面用户DBUS会话总线地址。总线地址可以很容易地获得并用于X11用户会话,但Wayland有更严格的安全性,对于Wayland来说,用户会话实际上必须运行代理才能接收消息。(你有没有考虑过发送电子邮件可能更容易?)
我已经在github上通知了桌面要点,它适用于X11,也应该适用于Wayland(前提是代理正在运行)。为了完整起见,我在这篇文章中附加了脚本的源代码,它得到了广泛的评论,我认为它包含了使您自己的代码工作所需的部分。
#!/bin/bash
# Provides a way for a root process to perform a notify send for each
# of the local desktop users on this machine.
#
# Intended for use by cron and timer jobs. Arguments are passed straight
# to notify send. Falls back to using wall. Care must be taken to
# avoid using this script in any potential fast loops.
#
# X11 users should already have a dbus address socket at /run/user/<userid>/bus
# and this script should work without requiring any initialisation. Should
# this not be the case, X11 users could initilise a proxy as per the wayland
# instructions below.
#
# Due to stricter security requirments Wayland lacks an dbus socket
# accessable to root. Wayland users will need to run a proxy to
# provide root with the necessary socket. Each user can must add
# the following to a Wayland session startup script:
#
# notify-desktop --create-dbus-proxy
#
# That will start xdg-dbus-proxy process and make a socket available under:
# /run/user/<userid>/proxy_dbus_<desktop_sessionid>
#
# Once there is a listening socket, any root script or job can pass
# messages using the syntax of notify-send (man notify-send).
#
# Example messages
# notify-desktop -a Daily-backup -t 0 -i dialog-information.png "Backup completed without error"
# notify-desktop -a Remote-rsync -t 6000 -i dialog-warning.png "Remote host not currently on the network"
# notify-desktop -a Daily-backup -t 0 -i dialog-error.png "Error running backup, please consult journalctl"
# notify-desktop -a OS-Upgrade -t 0 -i dialog-warning.png "Update in progress, do not shutdown until further completion notice."
#
# Warnings:
# 1) There has only been limited testing on wayland
# 2) There has only been no testing for multiple GUI sessions on one desktop
#
if [ $1 == "--create-dbus-proxy" ]
then
if [ -n "$DBUS_SESSION_BUS_ADDRESS" ]
then
sessionid=$(cat /proc/self/sessionid)
xdg-dbus-proxy $DBUS_SESSION_BUS_ADDRESS /run/user/$(id -u)/proxy_dbus_$sessionid &
exit 0
else
echo "ERROR: no value for DBUS_SESSION_BUS_ADDRESS environment variable - not a wayland/X11 session?"
exit 1
fi
fi
function find_desktop_session {
for sessionid in $(loginctl list-sessions --no-legend | awk '{ print $1 }')
do
loginctl show-session -p Id -p Name -p User -p State -p Type -p Remote -p Display $sessionid |
awk -F= '
/[A-Za-z]+/ { val[$1] = $2; }
END {
if (val["Remote"] == "no" &&
val["State"] == "active" &&
(val["Type"] == "x11" || val["Type"] == "wayland")) {
print val["Name"], val["User"], val["Id"];
}
}'
done
}
count=0
while read -r -a desktop_info
do
if [ ${#desktop_info[@]} -eq 3 ]
then
desktop_user=${desktop_info[0]}
desktop_id=${desktop_info[1]}
desktop_sessionid=${desktop_info[2]}
proxy_bus_socket="/run/user/$desktop_id/proxy_dbus_$desktop_sessionid"
if [ -S $proxy_bus_socket ]
then
bus_address="$proxy_bus_socket"
else
bus_address="/run/user/$desktop_id/bus"
fi
sudo -u $desktop_user DBUS_SESSION_BUS_ADDRESS="unix:path=$bus_address" notify-send "$@"
count=$[count + 1]
fi
done <<<$(find_desktop_session)
# If no one has been notified fall back to wall
if [ $count -eq 0 ]
then
echo "$@" | wall
fi
# Don't want this to cause a job to stop
exit 0