[Megoldva] Éppen melyik az aktuális display?

 ( locsemege | 2016. április 12., kedd - 12:45 )

Hogyan lehet scriptből megállapítani, éppen melyik display az, amelyiknek a képe a fizikai monitoron van? Tudom, nem egyszerű, mert több monitor esetén nem feltétlenül egy display és több screen a felállás, hanem több display is lehet.

A felvetés azért érdekes, mert valamit elszúrtak az emerald dekorátorban. Nem zavaróan gyakran, de úgy heti két alkalommal megdöglend. Akarok írni ey daemon-t, amelyik valahonnan tudná, hogy épp melyik az aktuális display - több egyidejűleg bejelentkezett felhasználó esetén ez egyáltalán nem biztos, hogy a :0 -, majd a who parancs kimenetéből ezen display-re keresve meg tudnám találni a hozzá tartozó felhasználót, így annak nevében a daemon újra tudná indítani az emerald dekorátort.

Megoldás:

#!/bin/bash

ACTIVE='
BEGIN {
    retval=1;
}

/^[ \t]*State:/ {
    if ($2=="active") {
        retval=0;
    }
    exit;
}

END {
    exit(retval);
}
'

AWKPROG='
BEGIN {
    retval=1;
}

$2 ~ /^:[[:digit:]]+$/ {
    print($1 " " $2);
    retval=0;
}

END {
    exit(retval);
}
'

COMPIZ_SCRIPT='
    killall -u "$USER" compiz-manager compiz emerald &>/dev/null
    echo "$USER: compiz-manager compiz emerald killed"
    sleep 5
    export DISPLAY=::
    USE_EMERALD=yes compiz-manager &>/dev/null </dev/null &
    echo "$USER: compiz-manager started"
    FROM=compiz
    SCRIPT="$HOME/.config/compiz-restart"
    SCRIPT="`readlink -f \"$SCRIPT\"`"
    if [ -f "$SCRIPT" -a -x "$SCRIPT" ]; then
        . "$SCRIPT"
    fi
'

VOLUMEICON_SCRIPT='
    export DISPLAY=::
    if pgrep -u "$USER" -x pulseaudio >/dev/null; then
        volumeicon &>/dev/null </dev/null &
        echo "$USER: volumeicon started"
    else
        killall -u "$USER" volumeicon &>/dev/null
        sleep 3
        killall -u "$USER" -SIGKILL volumeicon &>/dev/null
        echo "$USER: volumeicon killed"
    fi
'

export PATH='/bin:/usr/bin'

runtest() {
    local u="$1" ret=0
    while shift; [ $# -gt 0 ]; do
        if ! pgrep -u "$u" -x "$1" >/dev/null; then
            ret=1
            break
        fi
    done
    return $ret
}

change_disp() {
    sed "s/::/$1/g" <<<"$2"
}

is_active() {
    loginctl user-status "$1" | awk "$ACTIVE"
}

while :; do
    users="`who | awk \"$AWKPROG\"`"
    if [ ! -z "$users" ]; then
        while read USER DISPLAY; do
            is_active "$USER" || continue
            if ! runtest "$USER" compiz emerald; then
                s="`change_disp \"$DISPLAY\" \"$COMPIZ_SCRIPT\"`"
                su - "$USER" -c "$s"
            fi
            if ! runtest "$USER" pulseaudio volumeicon; then
                s="`change_disp \"$DISPLAY\" \"$VOLUMEICON_SCRIPT\"`"
                su - "$USER" -c "$s"
            fi
        done <<<"$users"
    fi
    sleep 10
done

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Nem értem.

Define "a fizikai monitor"...

A gépből nézve a következő fogalmak léteznek:
- lokális, valódi, beazonosítható grafikus kártyák (ebből ugye lehet 0 vagy több),
- az ezekre rákötött kimenetek (ebből ugye lehet kártyánként több),
- ezek közül van, amelyik támogat EDID-et, és van, amelyik kézzel van bedrótozva (ergó azt se tudni, hogy éppen van-e rádugva valami; pl. egy LVDS kimeneten ezt nem is lehet detektálni),
- ezek közül lehet olyan, amelyiket kezel éppen valami futó X szerver, és lehet olyan is, amit nem,
- távoli, X11 prokotollal elérhető felületek, ez ugye vagy a helyi gépen végződik (pl. 127.0.0.1:0), vagy nem, az SSH tunnelnek köszönhetően szintén nem lehet tudni semmit se kb.,
- virtuális grafikus kártyát használó X szerverek (pl. Xvnc), ahol nem is tudni, hogy hol végződik a megjelenítés (hiszen az lehet a hálózaton valahol máshol, ráadásul a random protokoll miatt pláne esély sincs kideríteni bármit), rááadásul 0 és végtelen között bármennyi megjelenítő lehet éppen rákötve.

Épp ez a bajom. Egyelőre megfogalmazni is nehéz. Nyilván az nem érdekel, ha valaki ssh -X-szel bejelentkezvén a gépemre futtat nálam valamit, amelynek az ablaka az ő monitorán jelenik meg. (Illetve de, érdekel, mert akkor felnyomták a gépem, de a probléma szempontjából nem érdekes ez az eset.)

Első körben arra szűkíteném a kérdést, hogy van néhány felhasználó bejelentkezve, mondjuk van egy, esetleg több monitor, de a monitorok egy adott pillanatban egy display különböző screen-jei legfeljebb. Bár, ha jól csalódom, a xinerama egy display egy screen-jén virtuálisan oldja ezt meg, de az csk jó. Ezen felhasználók egyike az, akinek a desktopja éppen látszik a monitor(ok)on. Az a kérdés, ki ő.

Avagy másképpen. Mondjuk a who parancs kimenetében user0 :0, user1 :1, user2 :2 van, s azt szeretném tudni, ezek közül melyik az, amelyiknek a képe a fizikai monitoron manifesztálódik színes pöttyök halmazaként. :)

Merre tapogatózzak, milyen paranccsal barátkozzak?


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

xrandr -q nem segít?

--
Coding for fun. ;)

Nem derül ki belőle az aktuális display.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Szerintem ezt nem tudod triviálisan lekérdezni. Mármint ha arról beszélünk, hogy pl. a gépen futó X :0 (Alt-F7) és X :1 (Alt-F8) közül melyik látszik éppen a gép egy szem konzolján.

Amit tehetsz, hogy az összes desktopra futtatod a feladatot, arra is, amit éppen senki nem néz. Én amúgy azt a processzt keresném meg, amelyikből indul a nevezett program (azt, ami a window manager helyett van ezekben az új izékben), és nem foglalkoznék a who paranccsal sem, hanem a kinézett session master processzből kinyerném az őt futtató uidot, meg az environment változóiból a DISPLAY-t, és ezeket a beállításokat használnám.

Néztem pstree-t, a compiz-manager vélhetően elforkolódott, mert a systemd (pid=1) az őse. Viszont azt jó ötletnek tartom, amit írtál. Kell egy ciklus, amelyik végigmegy az összes olyan felhasználón, akik grafikus felületen be vannak jelentkezve. Most nézem a dolgokat, önálló Xorg szerverek futnak, az nem derül ki, hogy a VGA interface-t melyikük használja éppen.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

A loginctl paranccsal kideríthető, hogy melyik az aktív munkamenet és melyik display-n fut.

Köszönöm a tippet, látom már, a loginctl user-status felhasználónév a megmondója. Ha a state active, akkor ő bámulja a képernyőt, ha online, akkor be van jelentkezve, de nem néz semmit. :)


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Hat ha mar shell, akkor ott a $DISPLAY es kesz. Az az aktualis eppen latott dolgok display-e.

Ezt te sem gondoltad végig :D

Hat ha inditok egy ilyet: Xephyr -ac -screen 1024x768 :16, majd azon inditok egy xterm-et es kiiratom a $DISPLAY parametert, akkor az visszaadja nekem hogy ":16". Ha pedig az eppen aktualis X sessionomben futtatok egy "echo $DISPLAY" parancsot az is visszadobja, hogy hol vagyok (nalam most ":0").

Sot, ha belepek egy masik felhasznaloval, mikozben en beloginolva maradok, akkor nala meg kiirja, hogy az o DISPLAY valtozoje eppen micsoda (nalam most a guest a ":1"-en van).

Szoval mit nem gondoltam at?

A DISPLAY valtozo megmondja, hogy mi a display. Ez volt a kerdes. :D

Nézted a scriptemet is? Az a gond, hogy a DISPLAY akkor mondja meg, ha onnan vagy belépve. Nekem viszont van egy root joggal systemd által futtatott daemon-om, amelyik lebeg a felhasználók és földhöz ragadt konzolaik, grafikus felületeik fölötti térben. Azt kellene megoldanom, hogy kiderítsem, az a kép, amit a monitor visszaad, vajon :0, :1, :2, szóval melyik display. Ebből az infóból ki tudom nyomozni a who parancs segítségével, hogy ki az a felhasználó, aki a monitor előtt ül. Ha ez megvan, ezzel a userrel csinálok egy su parancsot, s a nevében indíthatok például emerald-ot. Előtte épp nekem kell definiálnom és exportálnom a DISPLAY változót annak érdekében, hogy az illető nevében futó alkalmazás a megfelelő Xorg-hoz tartozó display-re kapcsolódjon.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Biztos, hogy jó irányból fogod meg ezt a feladatot?

Egyáltalán nem biztos. Valószínűleg a systemd.slice környékén lehet a megoldás, csak ahhoz tök vagyok. Mert, ha több felhasználó van bejelentkezve egyidejűleg, felhasználóváltásnál a pulseaudio hangszerverek is tudják, melyiküknek kell a fizikai hangkártyá(ka)t masszírozni. Egyébként hogyan csinálnád?

Tehát az alap gondolatom, hogy szeretnék egy olyan ördögöt, amelyik figyeli, hogy ki előtt van a grafikus felület, s ha még nem fut a compiz, emerald, akkor elindítja, ha meg megdöglött valamelyik, akkor újraindítja. Hasonlóképpen a volumeicon-nal is. Ott még meg kell írnom, hogy ha valaki leállítja a pulseaudio-t, akkor a volumeicon nyíródjék ki.

Kellene kezelni a felhasználók be-, kilépkedéseit, valamint azt az esetet, ha több felhasználó van belépve egyidejűleg, de váltogatják, ki van a monitor előtt. Az irreleváns, ki van bent ssh-n.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Az enlightenment-ben láttam olyan ablakot, hogy elszállt az ablakkezelő, újraindítom. (OK)
Ezt egy wrapperrel is meg lehet oldani, gondolom.

Teljesen automatizálni szeretném. A fenti scriptem systemd daemon-ként működik, csak borzasztó ronda, a legkevésbé sem általános. Azt vettem a fejembe, hogy kicsit rendbe szedem.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Kipróbáltam valamit...
mc.sh:

#!/bin/sh
function finish {
$0
}
trap finish EXIT
mc

Ha ezt elindítom és bezárom a terminált, a gép elkezd lassulni, végül használhatatlan lesz. A killall mc.sh segít valamit, de egy idő után megint elszaporodnak a folyamatok, és megeszik a teljes swap-ot is.
Arra még nem jöttem rá hogy gép újraindításon kívül mit lehet csinálni vele :-)

Ezt most miért írtad? Szerintem ez egy fork bomba. Amikor kilépnél, a signal handlerben visszahívod önmagát. Az megint kilépne, aminek következtében egy újabb hívás keletkezik, s ez így végtelen ciklusban zabálja fel a memóriát szerintem. A nyitóban lévő systemd daemon-om működik, picit még finomítottam rajta. Teszteltem, teljesen jó.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Csak érdekességképpen.

Én meg a nyitóba tettem a módosított scriptemet. A COMPIZ_SCRIPT bővült annyival, hogy a compiz újraindításakor felhasználónként külön script futtatására van lehetőség. A $HOME/.config/compiz-restart nevű file-t include-olom be, de csak akkor, ha van rá futtatási joga a felhasználónak. Azért így, mert ellenkező esetben futtatási jog nélkül futhatna az include-olt script, ami nem kicsit volna biztonsági rés.

Erre egyébként azért volt szükség, mert az emerald dekorátor újraindításakor a system tray-en pihenő qxkb és az alltray-be dokkolt timeline megdöglött, ezeket ilyenkor kinyírom, majd újra elindítom.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Biztos, hogy csak akkor omlik össze, ha a kép fizikai monitoron van?

Az nem jó, ha a démonod figyeli és megjegyzi, melyik felhasználó indított el compiz-managert, és ha egy compiz-manager processz eltűnik, visszapótolja a lista alapján? Ráadásul egy ilyen démont tetszőleges processzek újraindítására felhasználhatsz.

:)

Lényegében ezt csinálom. Az aktuális display tulajdonképpen csak azért lett volna érdekes, hogy futásidőt optimalizáljak. Ugyanakkor belátom, értelmetlen. Már az sem túl gyakori eset, hogy egyidejűleg ketten vannak grafikus felületen belépve egy desktopra.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Azon gondolkodom, hogy szerintem maga a koncepció hibás. Miért futtassak egy külön programot, hogy az detektálja, hogy egy másik lerohadt, csak azért, hogy ebben az esetben újraindítsa azt - ha ezt minden különösebb erőfeszítés nélkül is megtehetem?
Ez a bizonyos program elindul valahonnan és ez kiváló hely arra, hogy belenyúljunk az indításba...
1) gány megoldás, de meg sem kell keresni, honnan van indítva a renitens program: az eredeti programot átnevezzük, majd a helyén ugyanilyen néven egy bash script jön létre, amely:
- nyit egy ciklust
- elindítja az _eredeti_ programot, a $* segítségével az megkapja az összes paramétert is, mivel a mi scriptünk indítja, esélyesen megkapja az envirokat is, de tuti hogy annak az usernek a nevében fog futni, akiében kell
- a program lefutása után az exit status alapján eldönthető, hogy szabályos volt-e a leállás, vagy nem? Ha elszállt, akkor a ciklus indul előröl, így azonnal megvan a restart is. Ha szabályosan kilépett, akkor a shell script is kiléphet. Ha nagyon igényesek akarunk lenni, akkor az exit status vizsgálata előtt azt eltároljuk, majd a script is ezt a státuszkódot adja vissza. Ugyanakkor ez durmincsolás, egy mezei rendszer frissítés pofon vághatja és semmiképp sem szép.
2) megkeresve azt a helyet, ahonnan indul (esélyesen nem az .xinit, /etc/X11/xinit, stb, hiszen ezt már a login manager indítja), ott követünk el hasonló trükköt - pl. a fenti scriptet saját néven követjük el, így nem kell az eredetit felülcsapni, viszont a login manager már a mi scriptünket indítja.

Vagy ez nagyon ördögtől való ötlet?

Nem, de az első biztosan maintainelhetetlen, a második pedig akkor, ha nincs rá standard konfig lehetőség (ergó át kell hekkelj valamit, amit nem arra terveztek, hogy valaki belenyúljon).
Akkor van gond ugyanis, ha nyomsz egy frissítést a gépen, és elvesznek a módosításaid; a bináris kicserélése biztosan ilyen, az OS simán felül fogja vágni a következő frissítésnél.

Igen, nem véletlenül írtam, hogy az első nem igazán szép megoldás. De nem látom, mi lenne a probléma a második változattal? Ekkor egy külön bash script készül, ami egyedi névvel van ellátva - tehát a csomagkezelő nem fogja felül vágni, pláne nem, ha az /usr/local/[s]bin alá kerül. A login managernek meg saját konfigja van, ott egyszerűen be kell írni, hogy a windowmanager nem az eredeti program, hanem a saját készítésű script. Rendszer frissítéskor a csomagkezelő látni fogja, hogy az eredeti script megváltozott, ezért szól is és rád bízza, hogy akkor maradjon a régi, kerüljön fel az új - de mindenképpen jelez érte.

Nincs a koncepcióval semmi baj. Működik. :)

Miért futtassak egy külön programot, hogy az detektálja, hogy egy másik lerohadt, csak azért, hogy ebben az esetben újraindítsa azt - ha ezt minden különösebb erőfeszítés nélkül is megtehetem?

A te javaslatod is külön program futtatása, csak nem systemd által, hanem wrapper scriptként. Ha meg arra gondolsz, hogy terminálból lehet emerald-ot indítani, igen. Viszont szebb, ha ez automatikus, azon kívül lehet gondolni olyan felhasználóra, aki semmilyen parancsot sem adott a shellnek, és ezútán sem vágyik ezt megtanulni.

A 2-esre: nagyon nem kell keresni azt a helyet, a session indításakor a saját autostart scriptemből indult, de háttérbe tettem - & van a sor végén. Most innen kiszedtem, s a daemon foglalkozik ezzel.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ja, igen, ha valaki megütközne a scriptben lévő

export DISPLAY=::

sorokon, érdemes megfigyelni, hogy a ::-ot sed-del lecserélem az aktuális DISPLAY-re, szóval amikor már fut, akkor :0, :1, :2, vagy valami hasonló van ott.


tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE