问题
I have a script that runs from udev
when I plug in my external drive. It always worked. But after upgrading from Linux 3.8/Xorg 1.12/Mint 14 (Ubuntu 12.10 compatible) to Linux 3.11/Xorg 1.14/Mint 16 (Ubuntu 13.10 compatible), it doesn't work anymore.
The script still runs, but none of the commands that require the display work. I figured that out by quitting the udev
daemon and manually run udevd --debug
for verbose output (more below).
This script used to work in 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."
But not anymore in Mint 16/13.10. In case you are wondering about possible solutions, I gradually added stuff and now it looks like this:
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
But it still doesn't work. udevd --debug
still shows this:
'(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'
Note that any bash logic works. Echoing test vars to >>/tmp/test.log
works. It's just accessing the display that does not work anymore.
This is driving me crazy. What is the correct way to achieve this now?
Update 2013-12-20
So, in the previous Ubuntu, X
commands would automatically find it's way to the current X
using user.
Now, I seem to need these two things every time:
- On the
X
using user:xhost +si:localuser:root
- On the
root/udev
side:- Copy
X
using users'~/.Xauthority
file to/root
- Copy
This 'feels' like a step back in time. This only works scripted when I log in as the same user everytime, so I can copy the .Xauthority
file from that users' home when the script executes.
What 'trick' did the old Ubuntu use to have this done auto'magic'ally?
回答1:
Ok, I'm writing this answer to try and clarify the security model of the X server, as I understand it. I'm not an expert on the subject, so I may have got some (many?) things wrong. Also, many things are different in different distributions, or even different versions of the same distribution, as the OP noted.
There are two main ways to get authorized to connect to the X server:
- The
xhost
way (Host Access): The server maintains a list of hosts, local users, groups, etc. that are allowed to connect to the server. - The
xauth
way (Cookie based): The server has a list of randomly generated cookies, and anybody showing one of these cookies will be granted access.
Now, the distribution specific stuff...
When the X server is launch by the start-up system, it is usually passed a command line of the form -auth <filename>
. This file contains a list of initial cookies to be used for authorization. It is created before the X server is run using the xauth
tool. Then just after the X server, the login manager is launch, and it is instructed to read the cookie from this same file, so it can connect.
Now, when user rodrigo
logs in, it has to be authorized to connect to the server. That is done by the login manager, and it has two options:
- It does the equivalent to:
xhost +si:localuser:rodrigo
. - It generates another cookie, adds it to the server and passes it to the user. This passing can be done in two ways:
- It is written in the file
$HOME/.Xauthority
(home of the new user). - It is written somewhere else (
/var/run/gdm/auth-for-rodrigo-xxxx
) and the environment variableXAUTHORITY
is set to the name of that file.
- It is written in the file
Also, it can do both things. Some login managers even add the root user to the list of authorized users by default (as if xhost +si:localuser:root
).
But note that if you are not authorized to connect to the X server, you cannot add yourself to the list (running xhost +
for example). The reason is the same as why you cannot open a house doof from the outside without a key... That's true even if you are root!
Does it mean that the root user cannot connect to the server? Absolutely not! But to get to that first you have to know how is the logged user configured to connect to the server. For that run as the logged user:
$ xhost
It will show a message and the list of authorized users, hosts or groups, if any:
access control enabled, only authorized clients can connect
SI:localuser:rodrigo
Then run:
$ echo $XAUTHORITY
To see where the authorization file is saved. If it is empty, then it will be ~/.Xauthority
. Then:
$ xauth list :0
To see the list of your authorized cookies.
Now, if there are any cookie in the server, the root user should be able to connect making the XAUTHORITY environment variable point to the right cookie file. Note that in many setups, the cookie of the login manager is also kept around. Just look for it!
Another possibility for root access is to modify the Xsession
files to add the command xhost +si:localuser:root
and get permanent access. The details vary with the particular program used, but for gdm
you would simply add an executable script in /etc/gdm/Init/
with the xhost
command and it will be run automatically in the next boot.
PS: You can check your root access to the X server with sudo -i
, but note that some sudo
configurations may keep the DISPLAY
, XAUTHORITY
or HOME
variables and modify the results of the tests.
EXAMPLE: This script should be able to connect you to the X server as root
export DISPLAY=:0
export XAUTHORITY=`ls /var/run/gdm/auth-for-gdm-*/database`
xrandr #just for show
Naturally, the path for the XAUTHORITY
variable will depend on what login manager you are using (greeter). You can use the user file (you say it is in /home/redsandro/.Xauthority
but I'm not so sure). Or you can use the greeter cookie. To get the greeter cookie you can use the following command:
$ pgrep -a Xorg
Which in my system gives:
408 /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-gDg3Ij/database -seat seat0 -nolisten tcp vt1
So my file is /var/run/gdm/auth-for-gdm-gDg3Ij/database
. The gDg3Ij
is random and changes every time the server is restarted, that's why the ls ...
trick.
The nice thing of using the GDM cookie instead of the user is that it does not depend on the user logged in. It will even work with no user at all!
UPDATE: From your latest comment I see that your X server command is:
/usr/bin/X :0 -audit 0 -auth /var/lib/mdm/:0.Xauth -nolisten tcp vt8
So there is the name of the cookie used to start the login manager. If I'm correct, that should be available all the time, if you are able to read the file. And you are root, so, the following lines should be enough to get you access to the display as root:
export DISPLAY=:0
export XAUTHORITY=/var/lib/mdm/:0.Xauth
zenity --info --text 'Happy New Year'
回答2:
A quick search turned up the following:
X authentication is based on cookies -- secret little pieces of random data that only you and the X server know... So, you need to let the other user in on what your cookie is. One way to do this is as follows: Before you issue the su or sudo (but after having ssh'ed into the remote system if you are using ssh), request the cookie for the current DISPLAY that's connecting to your X server:
$ xauth list $DISPLAY You'll get something like
somehost.somedomain:10 mit-magic-cookie-1 4d22408a71a55b41ccd1657d377923ae
Then, after having done su, tell the new user what the cookie is:
$ xauth add somehost.somedomain:10 MIT-MAGIC-COOKIE-1 4d22408a71a55b41ccd1657d377923ae
(just copy and paste the output of the above 'xauth list' onto 'xauth add') That's it. Now, you should be able to start any X application.
For reference, here is the origin http://www.linuxquestions.org/questions/linux-newbie-8/xlib-connection-to-0-0-refused-by-server-xlib-no-protocol-specified-152556/
回答3:
This is not pretty, but I have not seen any solutions yet. So it's the best one so far.
- On the
X
using user:xhost +si:localuser:root
- On the root/udev side:
- Copy
X
using users'~/.Xauthority
file to/root
(* see note below)
- Copy
Now it works. Try zenity --warning --text=Hooray
This only works when you know which user is going to be logged into X. So it's only acceptable when the computer is being used by a single user with a single user account.
*) Note
This is notable, because I tried the documented ways of xauth merge /home/redsandro/.Xauthority
and $XAUTHORITY=/home/redsandro/.Xauthority
. These documented methods just plain do nothing these days, even if root
has permission to read it. You need to literally the whole .Xauthority
file in stead of just pointing to it.
回答4:
Newer versions of Ubuntu use different display managers, so you have to know which one you are using. In Rodrigo's post, there is a hint showing how to discover it, using this command:
ls /var/run/gdm/auth-for-gdm-*/database
To check this, list the /var/run directory and use the "pgrep -a Xorg" command. In Ubuntu 16* it'´s using sddm, so, you can use
ls /var/run/sddm* to export the XAUTHORITY variable.
The script would be like this:
#!/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
then run:
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
回答5:
I had to use this in Kali Linux 2016 to get it to work:
#!/bin/bash
set -x
xhost local:root
export DISPLAY=:0.0
su root -c 'zenity --notification --text="I am a notification!"'
来源:https://stackoverflow.com/questions/20395027/scripts-launched-from-udev-do-not-have-display-access-anymore