我有一个脚本,当我插入外部驱动器时,它从udev
运行。它总是有效。但是从Linux 3.8/Xorg 1.12/Mint 14(兼容Ubuntu 12.10(升级到Linux 3.11/Xorg 1.14/Mint 16(兼容Ubuntu 13.10(后,它不再起作用了。
脚本仍在运行,但需要显示的命令都不起作用。我通过退出 udev
守护程序并手动运行 udevd --debug
以获得详细输出(更多内容见下文(来解决这个问题。
此脚本曾经在 Mint 14/12.10 中工作:
export DISPLAY=:0
UUID=$1
DEV=$2
notify-send -t 700 "mounting $DEV ($UUID)"
gnome-terminal -t "Backing up home..." -x rsync long line of data
zenity --warning --text="Done."
但在铸币厂 16/13.10 中不再如此。如果您想知道可能的解决方案,我逐渐添加了东西,现在看起来像这样:
export DISPLAY=:0.0
xhost +local:
xhost +si:localuser:root
xhost +
DISPLAY=:0.0
export DISPLAY=:0.0
UUID=$1
DEV=$2
notify-send -t 700 "mounting $DEV ($UUID)"
gnome-terminal -t "Backing up home..." -x rsync long line of data
zenity --warning --text="Done." --display=:0.0
但它仍然不起作用。 udevd --debug
仍然显示这一点:
'(err) 'No protocol specified'
'(err) ''
'(err) '** (gnome-terminal:24171): WARNING **: Could not open X display'
'(err) 'No protocol specified'
'(err) 'Failed to parse arguments: Cannot open display: '
'(err) 'No protocol specified'
'(err) ''
'(err) '** (zenity:24173): WARNING **: Could not open X display'
'(err) 'No protocol specified'
'(err) ''
'(err) '(zenity:24173): Gtk-WARNING **: cannot open display: :0.0'
'(err) 'No protocol specified'
请注意,任何 bash 逻辑都有效。回显测试变量以>>/tmp/test.log
工作。它只是访问不再工作的显示器。
这让我发疯。现在实现这一目标的正确方法是什么?
更新 2013-12-20
因此,在以前的 Ubuntu 中,X
命令会自动找到使用用户当前X
的方式。
现在,我似乎每次都需要这两样东西:
- 在使用用户的
X
上:-
xhost +si:localuser:root
-
- 在
root/udev
方面:- 使用用户的
~/.Xauthority
文件将X
复制到/root
- 使用用户的
这"感觉"就像是时光倒流。这仅在我每次以同一用户身份登录时都适用于脚本,因此我可以在脚本执行时从该用户的家中复制.Xauthority
文件。
旧的 Ubuntu 用什么"技巧"来自动"神奇"地完成这项工作?
好的,据我所知,我写这个答案是为了尝试澄清 X 服务器的安全模型。我不是这方面的专家,所以我可能弄错了一些(很多?(事情。此外,正如 OP 所指出的那样,不同发行版中的许多内容甚至同一发行版的不同版本都是不同的。
有两种主要方法可以获得连接到 X 服务器的授权:
xhost
方式(主机访问(:服务器维护允许连接到服务器的主机、本地用户、组等的列表。xauth
方式(基于 Cookie(:服务器有一个随机生成的 cookie 列表,任何显示这些 cookie 之一的人都将被授予访问权限。
现在,分发特定的东西...
当启动系统启动 X 服务器时,通常会传递一个表单为 -auth <filename>
的命令行。此文件包含用于授权的初始 Cookie 列表。它是在使用 xauth
工具运行 X 服务器之前创建的。然后就在 X 服务器之后,登录管理器启动,并指示它从同一文件中读取 cookie,以便它可以连接。
现在,当用户rodrigo
登录时,必须授权它连接到服务器。这是由登录管理器完成的,它有两个选项:
- 它的作用相当于:
xhost +si:localuser:rodrigo
。 - 它生成另一个cookie,将其添加到服务器并将其传递给用户。可以通过两种方式完成此传递:
- 它写在文件
$HOME/.Xauthority
(新用户的家(中。 - 它被写在其他地方(
/var/run/gdm/auth-for-rodrigo-xxxx
(,环境变量XAUTHORITY
被设置为该文件的名称。
- 它写在文件
此外,它可以做这两件事。一些登录管理器甚至默认将root用户添加到授权用户列表中(好像xhost +si:localuser:root
(。
但请注意,如果您无权连接到 X 服务器,则无法将自己添加到列表中(例如,运行xhost +
(。原因和为什么没有钥匙就不能从外面打开房子一样......即使你是根,也是真的!
这是否意味着根用户无法连接到服务器?绝对不行!但是要首先做到这一点,您必须知道如何配置登录的用户以连接到服务器。对于以登录用户身份运行的:
$ xhost
它将显示一条消息以及授权用户、主机或组的列表(如果有(:
access control enabled, only authorized clients can connect
SI:localuser:rodrigo
然后运行:
$ echo $XAUTHORITY
查看授权文件的保存位置。如果它是空的,那么它将是~/.Xauthority
.然后:
$ xauth list :0
查看您的授权 Cookie 列表。
现在,如果服务器中有任何cookie,则root用户应该能够连接,使XAUTHORITY环境变量指向正确的cookie文件。请注意,在许多设置中,登录管理器的cookie也被保留在周围。就找吧!
root 访问权限的另一种可能性是修改Xsession
文件以添加命令xhost +si:localuser:root
并获得永久访问权限。详细信息因使用的特定程序而异,但对于gdm
,您只需使用 xhost
命令在/etc/gdm/Init/
中添加一个可执行脚本,它将在下次启动时自动运行。
PS:您可以使用 sudo -i
检查您对 X 服务器的 root 访问权限,但请注意,某些sudo
配置可能会保留DISPLAY
、XAUTHORITY
或HOME
变量并修改测试结果。
示例:此脚本应该能够以 root 用户身份将您连接到 X 服务器
export DISPLAY=:0
export XAUTHORITY=`ls /var/run/gdm/auth-for-gdm-*/database`
xrandr #just for show
当然,XAUTHORITY
变量的路径将取决于您使用的登录管理器(欢迎员(。您可以使用用户文件(您说它是/home/redsandro/.Xauthority
但我不太确定(。或者您可以使用迎宾饼干。要获取迎宾 cookie,您可以使用以下命令:
$ pgrep -a Xorg
在我的系统中,它给出了:
408 /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-gDg3Ij/database -seat seat0 -nolisten tcp vt1
所以我的文件/var/run/gdm/auth-for-gdm-gDg3Ij/database
.gDg3Ij
是随机的,每次重新启动服务器时都会更改,这就是ls ...
技巧的原因。
使用 GDM cookie 而不是用户的好处是它不依赖于登录的用户。它甚至可以在没有用户的情况下工作!
更新:从您的最新评论中,我看到您的 X 服务器命令是:
/usr/bin/X :0 -audit 0 -auth /var/lib/mdm/:0.Xauth -nolisten tcp vt8
因此,有用于启动登录管理器的cookie的名称。如果我是对的,如果您能够读取该文件,它应该一直可用。而且您是 root,因此,以下行应该足以让您以 root 身份访问显示:
export DISPLAY=:0
export XAUTHORITY=/var/lib/mdm/:0.Xauth
zenity --info --text 'Happy New Year'
快速搜索显示以下内容:
X身份验证基于cookie - 秘密的小块随机 只有您和 X 服务器知道的数据...所以,你需要让 其他用户了解您的 cookie 是什么。一种方法是 以下: 在你发出 su 或 sudo 之前(但在 ssh'ed 之后 远程系统(如果您使用的是 SSH(,请求 cookie 为 当前连接到 X 服务器的显示器:
$ xauth list $DISPLAY 你会得到类似的东西
somehost.somedomain:10 mit-magic-cookie-1 4d22408a71a55b41ccd1657d377923ae
然后,在完成 su 之后,告诉新用户 cookie 是什么:
$ xauth add somehost.somedomain:10 MIT-MAGIC-COOKIE-1 4d22408a71a55b41ccd1657d377923ae
(只需将上述"Xauth 列表"的输出复制并粘贴到"Xauth 添加'(就是这样。现在,您应该能够启动任何 X 应用程序。
作为参考,这里是起源 http://www.linuxquestions.org/questions/linux-newbie-8/xlib-connection-to-0-0-refused-by-server-xlib-no-protocol-specified-152556/
这并不漂亮,但我还没有看到任何解决方案。所以这是迄今为止最好的一个。
- 在使用用户的
X
上:-
xhost +si:localuser:root
-
- 在根/乌德夫端:
- 使用用户的
~/.Xauthority
文件将X
复制到/root
(*请参阅下面的注释(
- 使用用户的
现在它起作用了。试试zenity --warning --text=Hooray
这仅在您知道哪个用户将登录到 X 时才有效。因此,仅当计算机由具有单个用户帐户的单个用户使用时,它才可接受。
*( 注
这是值得注意的,因为我尝试了记录在案的xauth merge /home/redsandro/.Xauthority
和$XAUTHORITY=/home/redsandro/.Xauthority
方式。这些记录在案的方法如今什么也没做,即使root
有权阅读它。您需要从字面上看整个.Xauthority
文件,而不是仅指向它。
较新版本的 Ubuntu 使用不同的显示管理器,因此您必须知道您使用的是哪一个。在 Rodrigo 的帖子中,有一个提示显示了如何使用以下命令发现它:
ls /var/run/gdm/auth-for-gdm-*/database
要检查这一点,请列出/var/run 目录并使用 "pgrep -a Xorg" 命令。在 Ubuntu 16* 中,它使用 sddm,因此您可以使用
ls/var/run/sddm* 导出 XAUTHORITY 变量。
脚本将如下所示:
#!/bin/bash
export DISPLAY=:0
export XAUTHORITY=`ls /var/run/sddm*`
HDMI_STATUS="$(cat /sys/class/drm/card0-HDMI-A-1/status)"
USER="your username"
export XAUTHORITY=/home/$USER/.Xauthority
export DISPLAY=:0
if [ "$HDMI_STATUS" = connected ];
then
sudo -u $USER pactl set-card-profile 0 output:hdmi-stereo+input:analog-stereo
else
sudo -u $USER pactl set-card-profile 0 output:analog-stereo+input:analog-stereo
fi
exit 0
然后运行:
sudo chmod 755 /usr/local/bin/toggle-sound
echo 'ACTION=="change", SUBSYSTEM=="drm", RUN+="/usr/local/bin/toggle-sound"' | sudo tee /etc/udev/rules.d/99-hdmi-sound.rules
sudo udevadm control --reload-rules
我必须在Kali Linux 2016中使用它才能让它工作:
#!/bin/bash
set -x
xhost local:root
export DISPLAY=:0.0
su root -c 'zenity --notification --text="I am a notification!"'
如果直接从 udev 调用脚本不起作用,为什么不启动调用该脚本的 systemd 服务呢?
这是我的解决方案:
首先是 udev 规则,当拔出具有ID_PART_ENTRY_UUID的设备(或分区(时运行 media-storage-unplugged.service
/etc/udev/rules.d/storage-unplugged.rules:
ACTION=="remove", KERNEL=="sd[a-z][0-9]", ENV{ID_PART_ENTRY_UUID}=="replace-with-your-uuid", SYMLINK+="storage", RUN+="/usr/bin/systemctl --no-block start media-storage-unplugged.service"
/etc/systemd/system/media-storage-unplugged.service: (service file(
[Unit]
Description=Triggered when storage is unplugged
[Service]
Type=oneshot
ExecStart=/usr/local/bin/storage_unplugged
[Install]
WantedBy=multi-user.target
/usr/local/bin/storage_unplugged (在这里发挥创意(
#!/bin/bash
notify-send-to-user "storage unplugged"
exit 0
/usr/local/bin/notify-send-to-user
#!/bin/bash
function ns() {
#Detect the name of the display in use
local display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
#Detect the user using such display (NOTE: Didn't work on Arch linux since the "who" command doesn't show which display the user is using)
#local user=$(who | grep '('$display')' | awk '{print $1}' | head -n 1)
#Statically assign user:
local user="user" # Replace with your user
#Detect the id of the user
local uid=$(id -u $user)
sudo -u $user DISPLAY=$display DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus notify-send "$@"
}
ns "$@"
根据您的需求调整此方法:)