假设您想要运行一个需要 root 权限的图形配置工具。然而,您的 X 会话正在您常用的账户下运行。起初这可能看起来很奇怪,但是 X 服务器将 不 允许该工具访问您的显示。当 root 通常可以做任何事情时,这怎么可能呢?您又该如何解决这个问题呢?
让我们将其推广到您想要以用户 ID clientuser
运行 X 应用程序,但 X 会话是由 serveruser
启动的情况。如果您已经阅读了关于 cookies 的章节,那么很明显为什么 clientuser
无法访问您的显示:~clientuser/.Xauthority
不包含访问显示所需的正确 magic cookie。正确的 cookie 可以在 ~serveruser/.Xauthority
中找到。
当然,任何适用于远程 X 的方法也适用于来自不同用户 ID 的 X(特别是 slogin localhost -l clientuser
)。只是客户端主机和服务器主机恰好是同一台。然而,当两台主机相同时,有一些传输 magic cookie 的快捷方式。
我们假设您使用 su
来切换用户 ID。基本上,您必须编写一个脚本来调用 su
,但将 su
执行的命令用一些代码包装起来,这些代码执行远程 X 所需的操作。这些必要的操作是设置 DISPLAY
变量和传输 magic cookie。
设置 DISPLAY
相对容易;它只是意味着在运行 su 命令参数之前定义 DISPLAY="$DISPLAY"
。所以您可以直接这样做
su - clientuser -c "env DISPLAY=$DISPLAY clientprogram &"
这还不行,因为我们仍然需要传输 cookie。我们可以使用 xauth list "$DISPLAY"
来检索 cookie。这个命令恰好以适合反馈给 xauth add
命令的格式列出 cookie;这正是我们所需要的!
我们需要通过管道传递 cookie。不幸的是,通过管道向 su
命令传递东西并不容易,因为 su
想要从其标准输入读取密码。幸运的是,在 shell 脚本中,我们可以调整一些文件描述符,并完成它。
因此,我们围绕此编写一个脚本,通过 clientuser
和 clientprogram
进行参数化。让我们在编写脚本的同时稍微改进一下它,使其可读性降低但更健壮。它看起来像这样
#!/bin/sh
if [ $# -lt 2 ]
then echo "usage: `basename $0` clientuser command" >&2
exit 2
fi
CLIENTUSER="$1"
shift
# FD 4 becomes stdin too
exec 4>&0
xauth list "$DISPLAY" | sed -e 's/^/add /' | {
# FD 3 becomes xauth output
# FD 0 becomes stdin again
# FD 4 is closed
exec 3>&0 0>&4 4>&-
exec su - "$CLIENTUSER" -c \
"xauth -q <&3
exec env DISPLAY='$DISPLAY' "'"$SHELL"'" -c '$*' 3>&-"
}
我认为这是可移植的,并且在大多数情况下都能很好地工作。我现在能想到的唯一缺点是,由于使用了 '$*'
,command
中的单引号会搞乱 su
命令参数 (`'$*'`) 中的引用。如果还有其他严重的错误,请给我发邮件。
将脚本命名为 /usr/local/bin/xsu
,您可以这样做
xsu clientuser 'command &'
不可能更简单了,除非您去掉密码。是的,也有其他方法可以做到这一点 (`sudo`),但这里不是讨论这个的地方。
刚刚提到的微小的 xsu
脚本已成为一个更扩展的脚本 sux
的基础,该脚本显然已作为软件包进入了 Debian 发行版。
显然,任何适用于非 root 客户端用户的方法也适用于 root 用户。然而,对于 root 用户,您可以使其更加容易,因为 root 可以读取任何人的 ~/.Xauthority
文件。无需传输 cookie。您所要做的就是设置 DISPLAY
,并将 XAUTHORITY
指向 ~serveruser/.Xauthority
。所以您可以这样做
su - -c "exec env DISPLAY='$DISPLAY' \
XAUTHORITY='${XAUTHORITY-$HOME/.Xauthority}' \
command"
将其放入脚本中会得到类似这样的内容
#!/bin/sh
if [ $# -lt 1 ]
then echo "usage: `basename $0` command" >&2
exit 2
fi
su - -c "exec env DISPLAY='$DISPLAY' \
XAUTHORITY='${XAUTHORITY-$HOME/.Xauthority}' \
"'"$SHELL"'" -c '$*'"
将脚本命名为 /usr/local/bin/xroot
,您可以这样做
xroot 'control-panel &'
虽然,如果您已经设置了 xsu
,那么就没有真正的理由再这样做了。