9

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?

2 Answers 2

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}' 
11
  • What if you used pgrep Xorg instead. I have 2 running now so how would this handle multiples? Commented Feb 26, 2014 at 19:08
  • @slm You don't happen to have tried the code...? Commented Feb 26, 2014 at 19:10
  • Of course I did. I'm seeing that it only finds one. Commented Feb 26, 2014 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... Commented Feb 26, 2014 at 19:13
  • Maybe I'm confused, doesn't he want to see all the usernames that are running X? Commented Feb 26, 2014 at 19:22
3

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.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.