Показати сповіщення на всіх запущених X-дисплеях


16

Використовуючи командний рядок, я хотів би показати сповіщення на кожному запущеному X-дисплеї. (та працює консоль)

Щось на зразок:

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

Чи є програма, яка це зробить? Якщо ні, чи можна це реалізувати з bash?


1
Для людей, які приїжджають сюди через кілька років, у цій відповіді є проста функція notify_all, яка працює в Ubuntu 16.04 і може використовуватися в скриптах, запущених root.
mivk

Відповіді:


16

Ви можете надіслати повідомлення на всі консолі за допомогою командної стінки.

Для надсилання сповіщень під X є повідомлення-відправлення, яке надсилає сповіщення поточному користувачеві на поточному дисплеї. (З вашого питання, я думаю, ви вже знаєте це.) Ви можете на цьому розробитись за допомогою певного сценарію. В основному ви повинні з'ясувати, хто користувачі на яких X-дисплеях. Отримавши цю інформацію, ви можете використовувати сповіщення-надсилання так:

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

Де fschmitt є користувачем на дисплеї 0. Ви можете проаналізувати вихід команди "хто", щоб знайти всіх користувачів та їх відображення. Вихід виглядає приблизно так

[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)

Розумієте, є два користувачі, які виконують X-сеанси, markmerk3 на дисплеї 0 та seamonkey на дисплеї 1. Я думаю, що вам потрібно простувати за tty [0-9] *, а потім запевнити, що в кінці рядка є (: [0 -9.] *) Для позбавлення від консольних логінів та вилучення ідентифікатора відображення з рядка між дужками.


2
Команда whoповідомляє, хто ввійшов у систему та на якому X відображається вхід. Вам, можливо, доведеться дещо відфільтрувати його.
tante

1
Хоча, мабуть, краще просто використовувати цикл у сценарії оболонки, ви завжди можете зробити щось на кшталт who | awk '/\(:[0-9]+\)/ {gsub("[:|(|)]","");print "DISPLAY=:"$5 " sudo -u " $1 " notify-send \"Message\""}' | bash. Також ви можете побачити unix.stackexchange.com/questions/1596/…
Стівен D

8

Ця тема трохи стара, вибачте, але сподіваюся, що все-таки я можу додати щось корисне до теми. (також Йозеф Куфнер написав хороший сценарій, це було трохи задовго на мій смак, і він використовує PHP)

Також мені знадобився інструмент, як описано в оригінальному запитанні (щоб надіслати повідомлення всім активним користувачам X). На основі відповідей тут я написав цей маленький скрипт, доступний лише для bash, який шукає активних X-користувачів (використовуючи "хто"), а потім виконує сповіщення-відправлення для кожного активного користувача.

І найкраще: ви можете використовувати мій сценарій точно так само, як "сповістити-надіслати", з усіма його параметрами! ;-)

сповістити-надіслати все:

#!/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

Скопіюйте вищевказаний код у файл з назвою "notify-send-all", зробіть його виконуваним і скопіюйте його в / usr / local / bin або / usr / bin (як завгодно). Потім запустіть його, наприклад, як root у сеансі консолі, як це:

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

Я використовую його вже кілька місяців на різних машинах, і до цього часу у мене не було проблем, і я протестував це на робочих стільницях MATE та Cinnamon. Також успішно запускається в межах cron та anacron.

Я написав цей сценарій для / під ArchLinux, тому, будь ласка, скажіть, чи є у вас проблеми в інших дистрибутивах Linux або настільних комп’ютерах.


|egrep?? це команда egrep?
Sw0ut

@ Sw0ut, egrep - це дійсно команда. Але на сторінці людини grep (1) сказано, що egrep, fgrep та rgrep застаріли, і рекомендується використання їх еквівалентних форм "grep -E", "grep -F" та "grep -r".
rsuarez

Замість awk '{print $1$5}'цього краще використовувати awk '{print $1$NF}'так, щоб він не розбивався на деякі місцевості, де дата відформатована пробілами (наприклад, Jun 3замість 2017-06-03). Ось також версія для сповіщення конкретного користувача замість усіх користувачів: gist.github.com/shvchk/ba2f0da49bf2f571d6bf606d96f289d7
Шевчук

1
Чудово працює на Ubuntu після використання grep -Eта додавання /binдо контуру (див. Редагування). Сміливо повертайтеся, якщо заперечуєте
серв-ін

3

Мені це знадобилося і для деяких системних сповіщень. Ось моє рішення. Він сканує / proc, щоб знайти всі автобуси сеансу, а потім він виконує сповіщення-відправлення на кожному з них (один раз на шину). Усі аргументи передаються без змін до реального повідомлення-відправки.

#!/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

В Ubuntu 16.04 я хотів отримувати сповіщення від сценарію, що працює як root від crontab. Після встановлення змінних оточення sudo -u $userчомусь sh -c "..." $userне працював , але працює.

Тому я зараз використовую цю функцію:

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 
}

Як знайти змінну DBUS_SESSION_BUS_ADDRESS, ймовірно, залежить від вашого розповсюдження. У Ubuntu 16.04 він знаходиться в /run/user/$UID/dbus-session, який можна просто знайти. id -uвикористовується у наведеній вище функції для отримання UID від імені користувача, поверненого користувачем who.


Як ним користуватися? Можеш допомогти мені?
elgolondrino

0

Ось оновлення сценарію Енді: спосіб, за яким він визначив DBUS_SESSION_BUS_ADDRESS, не працює на Centos 7. Також whoкоманда чомусь не перераховує деякі сеанси, тому я ps auxзамість цього аналізую вихід. Цей сценарій передбачає, що користувачі входять у систему за допомогою X2GO ( nxagent), але для інших випадків його слід просто налаштувати.

#!/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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.