16

Using the command line, I'd like show a notification on every running X display. ( and running console )

Something like:

notify-send-all 'Warning' 'Nuclear launch in 5 minutes, please evacuate'

Is there a program that will do this? If not, can this be implemented with bash?

  • 1
    For people coming here years later, there is a simple notify_all function in this answer which works in Ubuntu 16.04, and can be used in scripts started by root. – mivk Feb 12 '17 at 11:48
16

You can send a message to all consoles with the command wall.

For sending notifications under X there is notify-send which sends a notification to the current user on the current display. (From your question, I guess you already know this one.) You can build upon this with some bash scripting. Basically you have to find out which users are on which X-Displays. Once you got this info you can use notify-send like this:

DISPLAY=:0 sudo -u fschmitt notify-send "Message"

Where fschmitt is the user at display 0. You can parse the output of the "who" command to find all users and their displays. The output looks like this

[edinburgh:~]$ who
markmerk3 tty7         2010-09-23 10:59 (:0)
markmerk3 pts/1        2010-09-30 13:30 (:0.0)
fschmitt pts/2        2010-10-08 11:44 (ip-77-25-137-234.web.vodafone.de)
markmerk3 pts/0        2010-09-29 18:51 (:0.0)
seamonkey pts/6        2010-09-27 15:50 (:1.0)
markmerk3 pts/5        2010-09-27 14:04 (:0.0)
seamonkey tty8         2010-09-27 15:49 (:1)
markmerk3 pts/13       2010-09-28 17:23 (:0.0)
markmerk3 pts/3        2010-10-05 10:40 (:0.0)

You see, there are two users running X sessions, markmerk3 at display 0 and seamonkey at display 1. I think you need to grep for tty[0-9]* then assure that at the end of the line there is (:[0-9.]*) to get rid of console logins and extract the display id from the string between the parentheses.

  • 2
    The command who tells you who is logged in and on which X display that login is. You just might have to filter it somewhat. – tante Oct 8 '10 at 9:49
  • 1
    While it is probably better to just use a loop in a shell script you could always do something like who | awk '/\(:[0-9]+\)/ {gsub("[:|(|)]","");print "DISPLAY=:"$5 " sudo -u " $1 " notify-send \"Message\""}' | bash. Also, you might want to see unix.stackexchange.com/questions/1596/… – Steven D Oct 8 '10 at 15:34
8

This thread is a bit old, sorry, but I hope I still can add something useful to the topic. (also Josef Kufner wrote a nice script, it was just a little bit too long for my taste, and it uses PHP)

I also needed a tool as described in the original question (to send a message to all active X-users). And based on the answers here, I wrote this little bash-only script, which searches for active X-users (using 'who'), and then running notify-send for every active user.

And the best: you can use my script exactly like "notify-send", with all of its parameters! ;-)

notify-send-all:

#!/bin/bash
PATH=/usr/bin:/bin

XUSERS=($(who|grep -E "\(:[0-9](\.[0-9])*\)"|awk '{print $1$5}'|sort -u))
for XUSER in $XUSERS; do
    NAME=(${XUSER/(/ })
    DISPLAY=${NAME[1]/)/}
    DBUS_ADDRESS=unix:path=/run/user/$(id -u ${NAME[0]})/bus
    sudo -u ${NAME[0]} DISPLAY=${DISPLAY} \
                       DBUS_SESSION_BUS_ADDRESS=${DBUS_ADDRESS} \
                       PATH=${PATH} \
                       notify-send "$@"
done

Copy the above code into a file named "notify-send-all", make it executable and copy it to /usr/local/bin or /usr/bin (as you like). Then run it e.g. as root in a console session like this:

notify-send-all -t 10000 "Warning" "The hovercraft is full of eels!"

I'm using it several months now, on different machines, and didn't have any problems so far, and I've tested it with MATE and Cinnamon desktops. Also successfully running it within cron and anacron.

I wrote this script for/under ArchLinux, so please tell me if you're having problems on another Linux distributions or desktops.

  • |egrep ?? is egrep a command ? – Sw0ut Apr 21 '17 at 9:06
  • @Sw0ut, egrep is indeed a command. But in the man page of grep(1) says that egrep, fgrep and rgrep are deprecated, and the use of their equivalent forms "grep -E", "grep -F" and "grep -r" is recommended. – rsuarez May 21 '17 at 16:49
  • Instead of awk '{print $1$5}' it is better to use awk '{print $1$NF}', so that it doesn't break on some locales where date is formatted with spaces (e.g. Jun 3 instead of 2017-06-03). Here is also a version to notify specific user instead of all users: gist.github.com/shvchk/ba2f0da49bf2f571d6bf606d96f289d7 – Shevchuk Jun 3 '17 at 19:11
  • 1
    Works splendidly on Ubuntu after using grep -E and adding /bin to the path (see the edit). Feel free to revert if you object – serv-inc Oct 18 '18 at 8:14
3

I needed this too for some system-wide notifications. Here is my solution. It scans /proc to find all session busses and then it executes notify-send on each of it (once per bus). All arguments are passed unchanged to real notify-send.

#!/bin/bash

/bin/grep -sozZe '^DBUS_SESSION_BUS_ADDRESS=[a-zA-Z0-9:=,/-]*$' /proc/*/environ \
| /usr/bin/php -r '
        $busses = array();
        array_shift($argv);
        while($ln = fgets(STDIN)) {
                list($f, $env) = explode("\0", $ln, 2);
                if (file_exists($f)) {
                        $user = fileowner($f);
                        $busses[$user][trim($env)] = true;
                }
        }
        foreach ($busses as $user => $user_busses) {
                foreach ($user_busses as $env => $true) {
                        if (pcntl_fork()) {
                                posix_seteuid($user);
                                $env_array = array("DBUS_SESSION_BUS_ADDRESS" => preg_replace("/^DBUS_SESSION_BUS_ADDRESS=/", "", $env));
                                pcntl_exec("/usr/bin/notify-send", $argv, $env_array);
                        }
                }
        }
' -- "$@"
1

In Ubuntu 16.04, I wanted notifications from a script running as root from crontab. After setting the environment variables, sudo -u $user didn't work for some reason, but sh -c "..." $user does work.

So I now use this function:

notify_all() {
    local title=$1
    local msg=$2

    who | awk '{print $1, $NF}' | tr -d "()" |
    while read u d; do
        id=$(id -u $u)
        . /run/user/$id/dbus-session
        export DBUS_SESSION_BUS_ADDRESS
        export DISPLAY=$d
        su $u -c "/usr/bin/notify-send '$title' '$msg'"
    done 
}

How to find the DBUS_SESSION_BUS_ADDRESS variable probably depends on your distribution. In Ubuntu 16.04, it is in /run/user/$UID/dbus-session, which can simply be sourced. id -u is used in the function above to get the UID from the username returned by who.

  • How to use it? Can you help me? – elgolondrino Apr 1 '17 at 17:09
0

Here's an update of Andy's script: The way it determined the DBUS_SESSION_BUS_ADDRESS does not work on Centos 7. Also the who command did not list some sessions for some reason, so I parse the ps aux output instead. This script assumes users are logged in using X2GO (nxagent), but it should be simple to adjust for other cases.

#!/bin/bash
PATH=/usr/bin:/bin
NOTIFY_ARGS='-u critical "Shutdown notice" "THE SYSTEM IS GOING DOWN TODAY AT 23:00.\nWe recommend you to save your work in time\!" -i /usr/share/icons/Adwaita/32x32/devices/computer.png -t 28800000'

function extract_displays {
    local processes=$1
    processes=$(printf '%s\n' "$processes" | grep -P "nxagent.+:\d+")
    ids=$(printf '%s\n' "$processes" | grep -oP "\W\K:(\d)+")
    echo $ids
}


function find_dbus_address {
    local name=$1
    PID=$(pgrep 'mate-session' -u $name)
    if [ -z "$PID" ]; then
        PID=$(pgrep 'gnome-session' -u $name)
    fi
    if [ -z "$PID" ]; then
        PID=$(pgrep 'xfce4-session' -u $name)
    fi

    exp=$(cat /proc/$PID/environ | grep -z "^DBUS_SESSION_BUS_ADDRESS=")
    echo $exp
}

PROCESSES=$(ps aux)
DISPLAY_IDS=$(extract_displays "$PROCESSES")
echo "Found the following DISPLAYS: $DISPLAY_IDS"
for DISPLAY in $DISPLAY_IDS; do
    NAME=$(printf '%s\n' "$PROCESSES" | grep -P "nxagent.+$DISPLAY" | cut -f1 -d ' ')
    DBUS_ADDRESS=$(find_dbus_address $NAME)
    echo "Sending message to NAME=$NAME DISPLAY=$DISPLAY DBUS_ADDRESS=$DBUS_ADDRESS"
    echo "NOTIFY_ARGS=$NOTIFY_ARGS"
    eval sudo -u ${NAME} DISPLAY=${DISPLAY} ${DBUS_ADDRESS} PATH=${PATH} notify-send $NOTIFY_ARGS
done
-1
users=$(who | awk '{print $1}')

for user in $users<br>
do
        DISPLAY=:0 sudo -u $user notify-send "hello!!"
done

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.