Kein /dev/tty, wenn das Skript als UDEV-Trigger aufgerufen wird

Kein /dev/tty, wenn das Skript als UDEV-Trigger aufgerufen wird

Ich habe Raspberry Pi mit installiertem Standard-Raspbian. Ich habe auch RetroPI installiert, das einwandfrei funktioniert.

Ich wollte ein kleines Autostart-Skript schreiben, das Emulationstation (Retropi-Hauptlaufskript) startet, wenn das Gamepad über Bluetooth verbunden ist. Ich habe eine einfache Udev-Regel erstellt.

pi@raspberrypi:~ $ cat /etc/udev/rules.d/99-zlocal.rules
SUBSYSTEM=="bluetooth",SUBSYSTEMS=="amba", ATTRS{id}=="00241011", ACTION=="add", RUN+="/usr/local/bin/emulator-controll.sh start"

das funktioniert wie erwartet, das Skript wird ausgeführt, wenn das Gamepad gekoppelt wird.

Das Problem ist, dass wenn ich mein Skript über SSH ausführe, wie

sudo emulator-controll.sh start        #sudo is optional here, but I want the same conditions as in udev trigger context

Emulationstation wie erwartet.Wenn jedoch dasselbe Skript als Aktionsauslöser für die obige udev-Regel verwendet wird, schlägt es fehlmit Fehlern wie

Wed Aug 21 00:02:33 CEST 2019
Invoking user: root
Invoked start command
Invoking emulation station start command
Done
'unknown': I need something more specific.
tput: unknown terminal "unknown"
Segmentation fault
/usr/bin/emulationstation: line 23: /dev/tty: No such device or address
tput: unknown terminal "unknown"

Ich glaube, dass aus irgendeinem Grund (ich bin ein Linux-Neuling) TTY vom Udev-Triggerkontext aus nicht zugänglich ist. Kann ich das irgendwie beheben?

PS. Ich habe versucht, einen Hintergrund-Daemon auszuführen (um benannte Pipes zu verwenden), der beim Booten gestartet wird, aber der Effekt ist genau der gleiche – kein TTY im Kontext der Skriptausführung, was mich glauben lässt, dass dies das allgemeine Verhalten von Hintergrundaufgaben ist

Das Skript selbst ist meiner Meinung nach irrelevant, aber ich werde es der Übersichtlichkeit halber ebenfalls angeben:

#!/bin/bash
#/bin/date >> /tmp/udev.log
#env >> /tmp/udev.log
#echo "Connected" > /dev/pts/2

log=/var/log/emulator-controll.log

{
readarray -t PIDS <<< $(ps aux | grep [e]mulationstation | awk '{print $2}')
echo ">${PIDS[@]}<"
echo >> $log
/bin/date >> $log
echo "Invoking user: $(whoami)"
case "$1" in
        start )
                echo "Invoked start command" >> $log
                if [[ "${#PIDS[@]}" -ge 3 ]]; then
                        echo "Emulator is already running. No action is taken" >> $log
                else
                        echo "Invoking emulation station start command" >> $log
                        export DISPLAY=:0
                        sudo -u pi nohup emulationstation  &
                fi
                echo "Done" >> $log
                ;;
        stop )
                echo "Invoked stop command" >> $log
                if [[ -z "${PIDS[0]}" ]]; then
                        echo "Emulator is not running, no action is taken" >> $log
                else
                        echo "Killing processes with PIDs ${PIDS[@]}" >> $log
                        for pid in "${PIDS[@]}"; do
                                kill $pid
                        done
                        echo "Done" >> $log
                fi
                ;;
        *)
                echo "Usage: $0 {start|stop}"
esac

} >> $log 2>>$log

Antwort1

Anstatt zu versuchen, ein Skript an TTY anzuhängen oder TTY dem Skript zuzuweisen, habe ich mein Ziel erreicht, indem ich den Befehl folgendermaßen direkt in den Eingabepuffer des Ziel-TTY eingefügt habe:

sudo writevt /dev/tty1 "emulationstation $(echo -ne '\r')"

Geben Sie dazu den Befehl ein und drücken Sie auf dem Hauptterminal die Eingabetaste.

PS. writevtist Teil von console-toolsund das ist auf Raspbian verfügbar (zumindest heute)

Antwort2

Wenn der Kernel udevim Rahmen der Regeln Aktionen auslöst und Skripte ausführt, ist die Umgebung dieser Skripte sehr eingeschränkt.

An einem einzigen Linux-Computer können mehrere Benutzer gleichzeitig angemeldet sein, jeder Benutzer mit mehreren TTYs.Natürlichein vom Kernel ausgeführtes Skript hat kein TTY: Dieses Skript wurde von keinem Benutzer gestartet. Selbst wenn man ihm also ein TTY zuweisen könnte, welches der vielen verwendeten TTYs sollte es sein? Und dasselbe gilt für eine X-Verbindung (was ebenfalls häufig gefragt wird).

Es gibt noch weitere Einschränkungen, wie Sie hier sehen können man 7 udev:

LAUFEN

Fügt ein Programm zur Liste der Programme hinzu, die für ein bestimmtes Gerät ausgeführt werden sollen. Dies kann nur für sehr kurz laufende Aufgaben verwendet werden. Das Ausführen eines Ereignisprozesses über einen langen Zeitraum kann alle weiteren Ereignisse für dieses oder ein abhängiges Gerät blockieren. Lang laufende Aufgaben müssen sofort vom Ereignisprozess selbst getrennt werden. Wenn die Option RUN{fail_event_on_error} angegeben ist und das ausgeführte Programm einen Wert ungleich Null zurückgibt, wird das Ereignis für eine mögliche spätere Bearbeitung als fehlgeschlagen markiert.

Wenn ich Ihr Skript richtig verstehe, möchten Sie auf einer bestimmten X-Verbindung etwas starten oder stoppen, das sehr lange laufen wird. Dies kann nur funktionieren, wenn Sie Ihre Aufgabe in zwei Aufgaben aufteilen: Lassen Sie eine Art Dämon starten, der gestartet wird, wenn der entsprechende X-Server gestartet wird (der X-Server muss gestartet werden, damit Sie das Autorisierungscookie erhalten können), und sagen Sie dem Dämon dann im udev-Ausführungsskript, dass er bestimmte Dinge tun soll, z. B. einen Dienst starten oder stoppen.

Auf diese Weise kann das Udev-Run-Skript sofort beendet werden.

Wenn Sie systemd verwenden, können Sie eine Unit auch über eine udev-Regel starten (siehe z. B.Hier).

Bearbeiten

Wenn Sie verwenden möchten , sollten Sie den darauf ausgeführten tty1Anmeldedienst ( ) in Ihren Dämon ändern, den Sie dann vom Skript aus auslösen können.gettytty1

Also zBHierEinzelheiten zu einer möglichen Vorgehensweise können je nach Bedarf variieren.

verwandte Informationen