How to send a message to running X Windows sessions in a multi-user Linux system

The ‘wall‘ command can be used to broadcast a message from a TTY console to other logged-in TTY console users in a multi-user Linux system. The Linux command ‘notify-send‘ can be used to send a message (a.k.a. notification) within an X Windows session, and Desktop Environments such as KDE and GNOME use notify-send to display pop-up notifications to the user. However, apparently no program exists in Linux to broadcast a message to other running X Windows sessions; that sounds like the sort of thing systemd developers would implement. A few years ago Unix & Linux Stack Exchange user Andy posted a Bash script notify-send-all to do just that (see Show a notification across all running X displays). For example, if you wanted to send a message to all the users of a multi-seat, multi-user Linux system who are currently logged-in to Desktop Environments, you could enter the following command to run the script in your home directory:

$ sudo ./notify-send-all -t 50000 "Warning" "Don't forget the staff meeting at 15:00 today."

Below is a slightly modified version of Andy‘s script that works for me in Lubuntu 18.04:

#!/bin/bash
PATH=/usr/bin:/bin
who|grep -E "\(:[0-9](\.[0-9])*\)"|awk '{print $1$5}'|sort -u > /tmp/xusers
while read XUSER; 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 < /tmp/xusers

Here is my tidied-up version:

#!/bin/bash
who | awk '{print $1, $NF}' | tr -d "()" | sort -u |
while read XUSER DISPNUM; do
    sudo -u $XUSER DISPLAY=$DISPNUM \
                   DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $XUSER)/bus \
                   notify-send "$@"
done

In Gentoo Linux DBUS_SESSION_BUS_ADDRESS needs to be found differently, and the following version of Andy‘s script works for me in that Linux distribution:

#!/bin/bash
PATH=/usr/bin:/bin
export $(dbus-launch)
who|grep -E "\(:[0-9](\.[0-9])*\)"|awk '{print $1$5}'|sort -u > /tmp/xusers
while read XUSER; do
    NAME=(${XUSER/(/ })
    DISPLAY=${NAME[1]/)/}
    sudo -u ${NAME[0]} DISPLAY=${DISPLAY} \
                       ${DBUS_SESSION_BUS_ADDRESS} \
                       PATH=${PATH} \
                       notify-send "$@"
done < /tmp/xusers

And here is my tidied-up version:

#!/bin/bash
export $(dbus-launch)
who | awk '{print $1, $NF}' | tr -d "()" | sort -u |
while read XUSER DISPNUM; do
    sudo -u $XUSER DISPLAY=$DISPNUM \
                   $BUS_SESSION_BUS_ADDRESS \
                   notify-send "$@"
done

notify-send-all will be of academic interest to users of single-user systems, but it’s nice to know such a thing is possible relatively easily in Linux.

About Fitzcarraldo
A Linux user with an interest in all things technical.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.