5

I'm implementing USB plug/unplug notification (here's related question), and I need to execute something like notify-send "device plugged" "My Device Title". The problem is that to make this command work, I should firstly set DISPLAY, like this:

export DISPLAY=":0.0"

And secondly, this command should be called by appropriate user. Say, for user dimon:

su dimon -c "notify-send 'device plugged' 'My Device Title'"

So, I need to get the list of all active X sessions and appropriate users, and call notify-send for each user on his DISPLAY.

I tried to use w for that, example output at Linux Mint 13 MATE:

USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
dimon    tty8     :0               Sun15    3days  1:38m  1.95s x-session-manager
dimon    pts/0    :0               Sun15    0.00s  0.20s  0.00s tmux

So we have both username and display. I decided to parse it like that:

declare -a logged_users=(`w |grep -vP "^(USER| )" |awk '{if (NF==8){print $1" "$3} else {print $1" :0"}}' |sort |uniq`)

Now, I have array logged_users: [0] contains dimon, and [1] contains :0. This would be great, but unfortunately it works not everywhere. Say, on Ubuntu 12.04 with lightdm we have this w output:

USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
nui      tty7                      18:22   35:56   1.66s  0.11s gnome-session -
nui      pts/0    :0.0             18:55    5.00s  0.20s  0.00s w

No idea why there's no FROM value for gnome-session. And even worse one, at xubuntu:

USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
nui      tty7                      15:50   31:07  52.55s  0.13s /bin/sh /etc/xd

No display at all! If other user is logged in, no display is specified for him too (but actually it's :1.0)

So, I'm looking for another approach. I also know we have a list of all active X sessions here: /tmp/.X11-unix , and I can get the list of them like this:

cd /tmp/.X11-unix && for x in X*; do echo ":${x#X}"; done

But then, I don't know how to retrieve users.

So, how to get the list of all active X sessions and appropriate users?

4

This is a solution for the users of local X servers (ignoring local X clients with remote or virtual X servers).

You determine the PID of the display manager (which is the parent PID of Xorg), determine the PIDs of its children and determine the user of all processes which have one of them as parent:

#! /bin/bash
xorg_pid=$(pidof -s /usr/bin/Xorg)
test -n "$xorg_pid" || exit 1
dm_pid=$(ps -eo pid,ppid,args | \
    awk -v xorg_pid=$xorg_pid '$1 == xorg_pid {print $2}')
pid_list="$(ps -eo pid,ppid,cmd | \
    awk -v dm_pid=$dm_pid '$2 == dm_pid {if (matchnr == 0) '\
'{ printf "%s%d ","$2 == ",$1; matchnr++;} '\
'else printf "%s%d ","|| $2 == ",$1;}')"
ps -eo pid,ppid,user,cmd | awk "$pid_list"'{print $3}'
  • What if you used pgrep Xorg instead. I have 2 running now so how would this handle multiples? – slm Feb 26 '14 at 19:08
  • @slm You don't happen to have tried the code...? – Hauke Laging Feb 26 '14 at 19:10
  • Of course I did. I'm seeing that it only finds one. – slm Feb 26 '14 at 19:10
  • @slm That's the intention. All instances have the same parent (at least that is the assumption of my script) thus using the first hit only is enough. I have two Xorg instances running, too. Would be difficult to test such code otherwise... – Hauke Laging Feb 26 '14 at 19:13
  • Maybe I'm confused, doesn't he want to see all the usernames that are running X? – slm Feb 26 '14 at 19:22
2

I had the same notify-send problem.

This method (also posted here) uses the environment information which ps e provides. ps e -u username | sed -rn 's/.* DISPLAY=(:[0-9]*).*/\1/p' outputs a list of all the DISPLAY numbers in the environments of all user username's processes.

If there is a root-owned window on your desktop then root will also have some processes with the same DISPLAY number, but otherwise one DISPLAY should only be associated with one user. (Not the other way round - right now I'm logged in at two tty's with two X sessions, so I am using both :0 and :1.)

This code will output all the currently used DISPLAYs (hence all the X sessions) of all the currently logged-in users. (root is skipped)

Note the sudo on the ps command: root permissions are needed to view the environments of other users' processes. Of course if the script is run by root it's unnecessary.

Usernames and display numbers are used as the indices of associative arrays to keep only unique values.

#!/bin/bash

declare -A disps usrs
usrs=()
disps=()

for i in $(users);do
    [[ $i = root ]] && continue # skip root
    usrs[$i]=1
done # unique names

for u in "${!usrs[@]}"; do
    for i in $(sudo ps e -u "$u" | sed -rn 's/.* DISPLAY=(:[0-9]*).*/\1/p');do
        disps[$i]=$u
    done
done

for d in "${!disps[@]}";do
    echo "User: ${disps[$d]}, Display: $d"
    #sudo -u "${disps[$d]}" DISPLAY="$d" notify-send "Title" "Message"
done

The final commented-out line shows how notify-send could be called.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.