Scripts launched from udev do not have DISPLAY access anymore?
Asked Answered
A

6

15

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

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?

Address answered 5/12, 2013 at 8:31 Comment(6)
Did you check the XAUTHORITY environment variable from a working X session? IIRC, Ubuntu changed the default path of the .Xauthority file.Sulfonate
It is still in ~ (and ENV set accordingly) - I believe it has always been in ~.Address
But if the script is run as root while the desktop session is started by another user, the ~ will be different. But sure you checked that!Sulfonate
You might want to try this question on serverfault.com, you may get better results.Falmouth
Yes, at least I think I got that covered. Aparently, root used to have access (as shown by the script that used to work without any extra auth options). Not anymore it seems, so that should be covered by xhost + afaik.Address
xhost + in the script will not work because you have to connect to the server to send the open-the-doors-to-everybody command. But you cannot connect to the server because you are not yet allowed. You'd have to do the xhost + from the logged-in session.Sulfonate
S
19

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 starts, the login manager is launched, 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 variable XAUTHORITY is set to the name of that 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 door 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? It certainly doesn't! But to get to that you first have to know how the logged-in user is allowed to connect to the server. For that, run as the logged-in 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 of those cookies 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'
Sulfonate answered 19/12, 2013 at 10:2 Comment(11)
I appreciate your effort to clarify all this X stuff, but after playing around with this new insight for half an hour, I still cannot seem to touch X from root in a different tts (for testing purposes). Therefor, although a nice summary, this is not the solution I am looking for. There is probably just one or two specific simple commands I need in my root script. I need them exactly, because I have failed figuring them out myself. Twice now.Address
@Redsandro: Ok, lets go step by step. First, did you determine which authentication method your X server uses? And if it is a xauth cookie did you determine for sure where that file is? To be totally sure -note that there may be old .Xauthority files around- delete it and try to open a X program from the user session: if it fails, that was it!Sulfonate
It uses the cookie method. And the environment variable points to the one in ~. If I copy that over to another user, that one too can access X. I'm not sure if I dare deleting the original file; didn't you just mention it might stop me from accessing X from that user too? I'm trying to get more X access here, not less. I'm not ready for that kind of commitment. :PAddress
@Redsandro: C'mon! This file is regenerated every time you log-in. And you can rename it to be extra safe. Please see my added script to see how I connect to X as root.Sulfonate
So what do you propose? The var already points to and proves the file in home is used. Also, I found out I am using MDM. I'm going to award you the +50 because of your efford, but I have the feeling you don't trust what I am saying just because it works different on your system, and that's not really an efficient way to communicate. I would appreciate if you edit in 'my' solution under a MDM heading and your solution under a GDM heading (although I cannot test it) or something. :)Address
Business announcement: Clicking the +50 does nothing. I can accept and upvote (and I did). Missing something?Address
@Redsandro: It's not a matter of trust... the thing is that I am pretty confident that these tools work as i think they do. And your tools must work just the same. It is difficult for me to know what's going on without access to your machine :-(, and you did not reply to some of my questions, so I'm guessing the best I can (and I cannot use MDM as it is not available for my OS). The single most important unanswered question is: what is the command line of the X server (ps -ef | grep Xorg)?.Sulfonate
I am very sorry, busy month with work and family (holiday), I am so quick to read that I missed the question. There is no Xorg in ps -ef. Do you mean X? root 5337 5264 11 23:09 tty8 00:03:23 /usr/bin/X :0 -audit 0 -auth /var/lib/mdm/:0.Xauth -nolisten tcp vt8Address
@Redsandro: Don't worry, we are all slow these days. And yes the X server has many names. Please see my updated answer.Sulfonate
Thank you. It works! Comparing this with my own answer (somewhere below), it turns out that the cookie in ~ (home) is the same cookie. Maybe it's left there for compatibility? I can't believe the answer was so simple and I was pretty close. I guess X (or only certain distros) made the default X security policy tighter for the old script not to work anymore. Thanks for your patience. You deserve the 50 rep. Don't spend it all in one club.Address
Great stuff! This answered my question here: #39010249Pages
F
1

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/

Falmouth answered 18/12, 2013 at 15:38 Comment(5)
This should be solved using xhost +, right? I tried that (see second script). Also, I think that any solution based on pre-2013 information is irrelevant because the script that used to work is based on those conventions.Address
That's not necessarily true, but my guess is they've added security concerns so that xhost + does not honor root.Falmouth
@Address xhost won't solve issues with xauth authentication. They're two completely different mechanisms. xhost is the older, simpler, and considerably less secure mechanism. xauth is newer, (somewhat) more secure, and a little harder to get right...Penmanship
Oh so any xhost (related) command is obsolete? (It's really hard to get uptodate information with google, there's hundreds of vague problems and sorta-but-not-really solutions mentioning xhost and xauth together.Address
@Address I'm not sure if xhost has ever really been declared obsolete... But in my experience, most distros and other Unixen configure xauth as the primary method these days... Both are still included and functional, though.Penmanship
A
1

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)

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.

Address answered 20/12, 2013 at 14:45 Comment(6)
That's simply not true. I have an up-to-date Xorg-server (1.14) and it works exactly as documented. My guess is that your user has a stale useless ~/.Xauthority file and the real one is elsewhere. For example in may system running as root xauth merge /run/gdm/auth-for-*/* will give m access to the X server no matter which user is logged in. /run/gdm/auth-for-gdm-*/* would work too, as the gdm is always there, but it is funnier that way.Sulfonate
You (and others) keep mentioning the real .Xauthority is somewhere else, but where? And why would the one in ~ be valid if it is stale?Address
This is "simply true" in the way that it "simply works". I don't know why. That's why I put up the +50 bounty.Address
I think you are mixing the solutions. if you do xhost +si:localuser:root as the logged-in user, then root will have access no matter what the XAUTHORITY variables and files. GDM (are you using it?) in Ubuntu no longer saves the .Xauthority in ~ but in /var/run/gdm/auth-for-*.Sulfonate
I don't really know about the technical details (hence my question), but the fact (the one that counts) is that this solution actually works. I prefer to have the old method where it works for any logged in user and not a hard-coded one, but I haven't seen (or found) any.Address
You might be right about the xhost, the solution could be either one or the other, not necessarily both.Address
A
1

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
Appose answered 9/2, 2017 at 10:22 Comment(0)
V
0

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!"'
Voluble answered 9/10, 2016 at 22:5 Comment(0)
B
0

If calling the script directly from udev doesn't work, why not start a systemd service which calls that script?

Here's my solution:

First is the udev rule that runs media-storage-unplugged.service when a device (or partition) that has ID_PART_ENTRY_UUID is unplugged

/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 (get creative here)

#!/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 "$@"

Adapt this method to your needs :)

Broker answered 10/3, 2022 at 23:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.