14

Is possible to send a message (popup window or something) to local user logged into X (xdm, fluxbox) from console ? For example: user1 is logged and using X/fluxbox, user2 logging into the same box by ssh to console. Now - what user2 have to do to send message to user1 ?

5 Answers 5

18

The xmessage tool lets you do exactly that; present a popup window to users with a text message inside.

Example: user2 logs into a BASH session on the box and issues:

export DISPLAY=':0.0'
xmessage "some message here"

if you log into a TSCH session, it'd be something like this:

setenv DISPLAY ':0.0'
xmessage "some message here'

Alternatively, you can send messages to terminal sessions in one of two ways:

Using wall (=warn all [users])

From the man page:
Wall displays the contents of file or, by default, its standard input, on the terminals of all currently logged in users.

If you want to target a specific user, use write. Again, man tells us:

The write utility allows you to communicate with other users, by copying lines from your terminal to theirs.

6
  • If a recipient (user1 in my example) is logged into X11, and haven't opened xterm or other terminal - will wall work too ? I think that user1 will not see messages.
    – marioosh
    Jul 13, 2011 at 7:33
  • I updated my answer to include the 'xmessage' solution. That will show a small window to the user, irrespective of whether they have a terminal open.
    – mjk
    Jul 13, 2011 at 7:36
  • 1
    Incidentally, 'apropos' is a useful utility to know; it searches manual pages for the text you pass to it. Try running 'apropos message' and see what it returns.
    – mjk
    Jul 13, 2011 at 7:44
  • I know xmessage but how to run it by user1 and get this small window opened on user2's screen ? is it even possible ?
    – marioosh
    Jul 13, 2011 at 7:53
  • @marioosh I would also like to know how to do that. Did you ever get it figured out?
    – user527325
    Nov 28, 2015 at 20:35
3

As a rule, X11 sessions are usually started in a “private” mode, so you require one of two things: either, coöperation from user2 to allow you to access their screens, or superuser privileges to “break in” to their screens.

DANGER: Do keep in mind, once you have access to their screen, you could also do things like capture keystrokes (potentially including passwords) and take screenshots, so this is a big security risk.

In most cases it's preferable to instead use a messaging tool or chat program for these things.

Caveats aside:

Coöperating

If user2 wants to allow user1 to access their screen, then first user2 must issue a command like:

 xhost +si:localuser:user1

This could potentially be added to a start-up script of some kind; I don't know Fluxbox, but I assume it has a rc or session-start hook.

Once this has been issed, user1 can then write to user2's display, if they can find it.

If user2 is aware of this, they can always send it to user1 in some other way.

Finding another user's Display

On a typical workstation, it's unusual to have more than one or two users logged in; in which case, “guessing” that the display you want is 0, 1, or maybe 2 might be a workable solution.

However, for a more complete solution, let's assume that you have a machine where very many users could be signed in, and try to find the one you want.

for proc in /proc/[0-9]*/
do
     if [ Xorg = $(< $proc/comm ) ] 2>/dev/null && \
        [ $(id -u user2) -eq $(< $proc/loginuid) ] 2>/dev/null
     then
        for subproc in $(
        do
            echo "user2 display=" $(perl -ne 'if (m,DISPLAY=(.*)\0,) { print $1 }' < $subproc/environ)
        done
     fi
done

then, user1 can finally (using the DISPLAY value obtained above; I've just written :14.0 here…)

DISPLAY=:14.0 xmessage "Hello, user2"

Sending a Notification instead

In the interests of completeness, the following works on Gnome desktop, at least. I don't know if Fluxbox has a notification widget.

 DISPLAY=:14.0 notify-send "Hello, user2"

This has a lot more user-friendly options for display, and probably will pop up in a rather friendlier way on the user's screen.

3
  • 3
    for subproc in what? I think your script is corrupted here.
    – Stéphane
    Feb 6, 2019 at 19:46
  • I'd like to use notify-send (which is how I ended up here), but using DISPLAY=:N.n notify-send only works if I do it as the user I want to send it to -- and if I do it from the GUI, lol (meaning it is totally useless). Otherwise notify-send either does nothing or blocks indefinitely. Did you actually test this when you wrote it?
    – goldilocks
    Jan 13, 2021 at 23:35
  • It was working with whatever version of Gnome+Fedora were current in 2016, but I haven't rechecked since. I often use notify-send from scripts but usually as "myself" … I suppose some sudo magic could work if the dbus system is more locked-down now?
    – BRPocock
    Jan 19, 2021 at 15:09
2

This is a slightly "off the wall" suggestion.

Install espeak (or espeak-ng) on the target machine from your ssh execute

 espeak "The matrix has you"

Espeak is a text to speech engine, and will output to the console's speakers. If the volume is not set to zero, this will cause the computer to speak to the local user.

1
  • 2
    pun intended...
    – James K
    Jun 13, 2020 at 11:39
2

For anyone who could not get notify-send to work by setting DISPLAY to that of the target user, you can use that or any other command by reading from a pipe that messages can be sent to.

#!/bin/bash

dir=/home/you/notify-pipe/
fifo=$dir/notifications

if [ ! -e $fifo ]; then 
# The '$dir' path must exist or mkfifo will fail.
    if [ ! -e $dir ]; then 
        mkdir -p $dir
        if [ $? -ne 0 ]; then exit $?; fi
    fi
    mkfifo -p $fifo
    if [ $? -ne 0 ]; then exit $?; fi
fi

echo $BASHPID > $dir/bash.pid
# All errors etc. from here on logged
exec &>> $dir/log.txt
date

while [ -e $fifo ]; do
    read line < $fifo
    notify-send "$line"
done 

You can then run this in the background with setsid -f ./whatever.sh (or autostart it via your DE). Other users and system processes can then notify by writing to the FIFO (eg. "echo hello > /home/you/notify-pipe/notifications"). If your home is not publicly readable the pipe can be anywhere.

To stop it you'll have to kill it or delete the pipe. Beware that a pitfall of this is that if the pipe exists but nothing is reading from it (eg. the script), writing a message (eg. via echo) will block until something does read it.

3
  • Nice! But what it is bash.pid used for? Nov 16, 2021 at 10:28
  • It seems that mkfifo in GNU coreutils 8.32 does not create intermediate path segments, and there is no such -p option like mkdir -p. So it will fail if $dir does not exists. Nov 16, 2021 at 12:24
  • Thank you -- probably worth noting. I've added some checks and comments about this.
    – goldilocks
    Nov 16, 2021 at 19:58
0

DISCLAIMER: The following is based on sending graphical notifications to the user via notify-send. This is a slightly modified version of the @goldilocks answer.

  • It takes care of creating any intermediate directory for the fifo file.

  • It will work posting all missed notifications written to the queue when the reading script is stopped (I don't know why but in the original one I was only getting one notification when the read side was stopped).

    This should allow any user with write permissions to the destination user's queue to send a notification disregarding the recipient is logged or not. You can think in notifications from root/systemd for example. BUT anyway, I don't think this "pending" notifications are persistent, as the sender process needs to be alive for the recipient to receive it. So, if I'm not wrong a reboot or logout (in case of being another user) will make it a dead letter.

File: ~/.config/autostart/start-notification-queue (make it executable chmod +x)

#!/usr/bin/env bash

set -euo pipefail

QUEUE=$HOME/.cache/notification-queue

# ---

basedir=$(dirname "$QUEUE")

[[ ! -d "$basedir" ]] && mkdir -p "$basedir"
[[ ! -e "$QUEUE" ]]   && mkfifo "$QUEUE"

while [[ -e "$QUEUE" ]]; do
  while read line; do
    notify-send "[Notification queue]" "$line"
  done < "$QUEUE"
done

How to auto start it

You can use Freedesktop (XDG) autostart spec to start the queue listener upon desktop startup. Just place the following file and your desktop environment should start it.

File: ~/.config/autostart/notification-queue.desktop

[Desktop Entry]
Name=Start user notification queue
Type=Application
Exec=bash -c '$HOME/.config/autostart/start-notification-queue'
Terminal=false

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .