Remote Pop-Up Meldungen erzeugen
Pop-Up Meldungen auf dem Desktop können gerade in der Administration ein wichtiges Mittel sein, um z.B. den User auf bevorstehende Wartungsarbeiten hinzuweisen. Leider gibt es in Linux (wie so häufig) nicht immer DIE standardisierte Lösung dafür parat. Um eine Möglichkeit (von womöglich vielen) aufzuzeigen, soll dieser Beitrag ein Leitfaden sein.
Pop-Ups in der grafischen Oberfläche erzeugen
Der nachfolgende Lösungsansatz basiert auf der Grundlage des folgenden Forenbeitrags des Users ‚cprn‘:
https://unix.stackexchange.com/questions/117083/how-to-get-the-list-of-all-active-x-sessions-and-owners-of-them
Die eigentliche Benchrichtigungsdienst, zur Erstellung der Pop-Ups auf den Desktops, lautet ’notify-send‘ und sollte auf so ziemlich jeder Ubuntu, Centos, Arch etc. Installation bereits vorhanden sein.
Da ich trotz der guten Lösung dieses Beitrags auf ein paar Hindernisse gestoßen bin, wurden einige Detailanpassungen meinerseits durchgeführt.
Um das nachfolgende Skript ausführen zu können, müssen zuvor 2 Pakete installiert werden:
sudo apt install dbus-x11
sudo apt install notification-daemon
Anschließend erzeugen wir uns die Datei …
nano ~/pop_up.sh
… und benutzen diesen Code, die Erklärung folgt im Anschluß:
#!/bin/bash
title="Titel"
message="Textkörper"
### Create temporary directory
mkdir -p /tmp/notification_temp/
### Read dbus notification services in array 'dbus_array'
unset dbus_array
declare -A dbus_array; dbus_array+=$(ls /usr/share/dbus-1/services/ -I org.freedesktop.Notifications.service | grep -i notif)
### Move dbus services to another location, except 'org.freedesktop.Notifications.service'
if [ -z "${dbus_array[@]}" ]; then
:
else
find /usr/share/dbus-1/services/ -iname "*notif*" ! -name "org.freedesktop.Notifications.service" -exec mv {} /tmp/notification_temp/ \;
fi
NOTIF_DBUS="/usr/share/dbus-1/services/org.freedesktop.Notifications.service"
if [ ! -f "$NOTIF_DBUS" ]; then
cat > /usr/share/dbus-1/services/org.freedesktop.Notifications.service <<EOF
[D-BUS Service]
Name=org.freedesktop.Notifications
Exec=/usr/lib/notification-daemon/notification-daemon
EOF
fi
declare -A disps usrs
usrs=()
disps=()
for i in $(ls /home/);do
usrs[$i]=1
done
for i in "${!usrs[@]}"; do
for n in $(sudo ps e -u "$i" | sed -rn 's/.* DISPLAY=(:[0-9]*).*/\1/p'); do
disps[$n]=$i
done
done
for d in "${!disps[@]}";do
sudo -u "${disps[$d]}" DISPLAY="$d" notify-send $title $message
done
### Move back dbus services
if [ -z "${dbus_array[@]}" ]; then
:
else
for i in "${dbus_array[@]}"; do mv /tmp/notification_temp/$i /usr/share/dbus-1/services/ ; done
fi
In den oberen Zeilen 3 und 4 wird zunächst der Titel und Textkörper Pop-Up Meldung definiert.
Eine der angesprochenen Hindernisse über die ich gestolpert bin, ist der Notification.Service, der in einigen Fällen nämlich gar nicht eingerichtet war. Dies ist jedoch notwendig, damit die Kommunikation zwischen dem X-Server (grafische Oberfläche) und dem Benachrichtigungsdienst funktioniert. Aus diesem Grund wird in den Zeilen 17-24 abgefragt, ob ein solcher Dienst im dbus (Schnittstelle zur Interprozesskommunikation) vorhanden ist. Sollte dem nicht so sein, wird ein entsprechender Eintrag angelegt. In meinem Fall befindet sich der ’notification-daemon‘ im Pfad ‚/usr/lib/notification-daemon/notification-daemon‘, was jedoch variieren kann. In diesem Fall, sollte die Zeile 18 entsprechend angepasst werden.
Fehlen diese Zeilen, kann es zu folgender Fehlermeldung kommen:
GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.Notifications was not provided by any .service files
Als nächstes werden in den Zeilen 30-31 zwei Arrays definiert: ‚usrs‘ und ‚disps‘. Diese werden später dazu benötigt um die zu benachrichtigenden Benutzer samt ihrer benutzten Display-Ports in Variablen zwischenzuspeichern.
Die erste Schleife von Zeile 33-35 liest sämtliche Namen des /home/-Verzeichnisses ein und speichert die Werte jeweils als Eintrag in das Array ‚usrs‘. Zwar werden hierbei ALLE Benutzer eingelesen, die sich jemals auf der Maschine eingeloggt haben (und ein Home-Verzeichnis besitzen) und nicht nur die derzeit angemeldeten. Doch gibt es auch einige X-Terminal Lösungen wie z.B. X2GO oder XRDP, die über ihren eigenen Dienst betrieben werden und nicht zuverlässig weitere angemeldete Benutzer anzeigen.
Weiter wird in Zeile 37 zunächst das eben erzeugte Array mit den Benutzern eingelesen und als Variable an die nächste untergeordnete Schleife übergeben, die ihrerseits nach sämtlichen, vom Benutzer gestarteten Prozessen, filtert und mit Hilfe von ein wenig sed-Filtermagie den verwendeten Display-Port in die Variable ’n‘ speichert. Die Benutzer (Variable ‚i‘) werden nun durch in das Array der Display-Port Variablen Überführt (Variable ’n‘), sodass ein assoziatives Array entsteht, die die Display-Port Variable als Schlüsselwort trägt (Zeile 39). Zu diesem Zeitpunkt ist im ‚disps‘-Array also eine eindeutige Zuordnung zwischen dem Display-Port und den Benutzern entstanden.
Zeile 43-45 erzeugt nun im großen Finale das gewünschte Pop-Up Fenster mit dem ’notify-send‘ Kommando. Für jedes Key-Word des ‚disps‘-Arrays (also der Display-Port) wird der Befehl in Zeile 44 ausgeführt. Dieser verwendet (neben besagtem Key-Word) auch den zugeordneten Wert (Benutzer). Dieser Wert lässt sich abfragen, Indem das vorangestellte ‚!‘ nicht mitgegeben wird (also: „${!disps[@]}“ = Key-Word = Display-Port / „${disps[@]}“ = Zugeordneter Wert = Benutzer).
Starten wir das Skript nun könnte das Ergebnis folgendermaßen aussehen:
Sollte eine Fehlermeldung erscheinen und ähnlich wie diese aussehen …
Error spawning command line „dbus-launch –autolaunch=ddd9946ecf04bb4b63ade5e9795c5f0 –binary-syntax –close-stderr“: Child process exited with code 1
Mag dies an einem fehlerhaften dbus Start des Notification.Services liegen. Um dies vorzubeugen, werden die dbus-Benachrichtigungsdienste temporär woanders abgelegt, die Pop-Up Meldung erzeugt und anschließend wieder zurückkopiert (Zeile 9-14 und 47-52).
Pop-Ups Remote erzeugen
Dieses Skript auf jeder Maschine auszuführen ist ein wenig müheselig. Möchte man den Prozess zentralisieren, lässt sich das Skript auch Remote über SSH aufrufen (SSH root Zugang vorausgesetzt). Hierzu dient folgender Befehl:
ssh root@ubuntu-pc 'bash -s' < ./pop_up.sh
Da wir hierdurch lediglich eine Maschine gleichzeitig ansteuern können, lässt sich auch eine Liste definieren …
nano ~/hosts.txt
### Inhalt der hosts.txt
ubuntu-pc
ubuntu-server
… die eingelesen und abgearbeitet wird:
for i in $(cat hosts.txt); do ssh root@$i 'bash -s' < ./pop_up.sh; done
Zum Schluß noch ein Zusatztipp:
Sollte folgende Fehlermeldung erscheinen ‚Exceeded maximum number of notifications on linux‘, nutzt das Kommando:
killall notification-daemon